diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index 6c8a4fd1c2..32447d1d61 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -776,25 +776,8 @@ void AudioMixer::run() {
// if the stream should be muted, send mute packet
if (nodeData->getAvatarAudioStream()
&& shouldMute(nodeData->getAvatarAudioStream()->getQuietestFrameLoudness())) {
- static const int TIME_BETWEEN_MUTES = 5; // in secs
- if (usecTimestampNow() - nodeData->getAvatarAudioStream()->getLastMuted() >
- TIME_BETWEEN_MUTES * USECS_PER_SECOND) {
- int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
- int packetSize = headerSize + sizeof(glm::vec3) + sizeof(float);
-
- // Fake data to force mute
- glm::vec3 position = nodeData->getAvatarAudioStream()->getPosition();
- float radius = 1.0f;
-
- char* packet = (char*)malloc(packetSize);
- populatePacketHeader(packet, PacketTypeMuteEnvironment);
- memcpy(packet + headerSize, &position, sizeof(glm::vec3));
- memcpy(packet + headerSize + sizeof(glm::vec3), &radius, sizeof(float));
-
- nodeList->writeDatagram(packet, packetSize, node);
- nodeData->getAvatarAudioStream()->setLastMutedNow();
- free(packet);
- }
+ QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeNoisyMute);
+ nodeList->writeDatagram(packet, node);
}
if (node->getType() == NodeType::Agent && node->getActiveSocket()
diff --git a/assignment-client/src/audio/AvatarAudioStream.cpp b/assignment-client/src/audio/AvatarAudioStream.cpp
index cd67722a2e..90dcefa09d 100644
--- a/assignment-client/src/audio/AvatarAudioStream.cpp
+++ b/assignment-client/src/audio/AvatarAudioStream.cpp
@@ -14,8 +14,7 @@
#include "AvatarAudioStream.h"
AvatarAudioStream::AvatarAudioStream(bool isStereo, const InboundAudioStream::Settings& settings) :
- PositionalAudioStream(PositionalAudioStream::Microphone, isStereo, settings),
- _lastMuted(usecTimestampNow())
+ PositionalAudioStream(PositionalAudioStream::Microphone, isStereo, settings)
{
}
diff --git a/assignment-client/src/audio/AvatarAudioStream.h b/assignment-client/src/audio/AvatarAudioStream.h
index e1fb6dd486..482c6fd538 100644
--- a/assignment-client/src/audio/AvatarAudioStream.h
+++ b/assignment-client/src/audio/AvatarAudioStream.h
@@ -20,17 +20,12 @@ class AvatarAudioStream : public PositionalAudioStream {
public:
AvatarAudioStream(bool isStereo, const InboundAudioStream::Settings& settings);
- qint64 getLastMuted() const { return _lastMuted; }
- void setLastMutedNow() { _lastMuted = usecTimestampNow(); }
-
private:
// disallow copying of AvatarAudioStream objects
AvatarAudioStream(const AvatarAudioStream&);
AvatarAudioStream& operator= (const AvatarAudioStream&);
int parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples);
-
- qint64 _lastMuted;
};
#endif // hifi_AvatarAudioStream_h
diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index 26cf94269b..21e18e41ba 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -1017,10 +1017,13 @@ void OctreeServer::readConfiguration() {
readOptionBool(QString("debugReceiving"), settingsSectionObject, _debugReceiving);
qDebug("debugReceiving=%s", debug::valueOf(_debugReceiving));
+ readOptionBool(QString("debugTimestampNow"), settingsSectionObject, _debugTimestampNow);
+ qDebug() << "debugTimestampNow=" << _debugTimestampNow;
+
bool noPersist;
readOptionBool(QString("NoPersist"), settingsSectionObject, noPersist);
_wantPersist = !noPersist;
- qDebug("wantPersist=%s", debug::valueOf(_wantPersist));
+ qDebug() << "wantPersist=" << _wantPersist;
if (_wantPersist) {
QString persistFilename;
@@ -1029,6 +1032,30 @@ void OctreeServer::readConfiguration() {
}
strcpy(_persistFilename, qPrintable(persistFilename));
qDebug("persistFilename=%s", _persistFilename);
+
+ _persistInterval = OctreePersistThread::DEFAULT_PERSIST_INTERVAL;
+ readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval);
+ qDebug() << "persistInterval=" << _persistInterval;
+
+ bool noBackup;
+ readOptionBool(QString("NoBackup"), settingsSectionObject, noBackup);
+ _wantBackup = !noBackup;
+ qDebug() << "wantBackup=" << _wantBackup;
+
+ if (_wantBackup) {
+ _backupExtensionFormat = OctreePersistThread::DEFAULT_BACKUP_EXTENSION_FORMAT;
+ readOptionString(QString("backupExtensionFormat"), settingsSectionObject, _backupExtensionFormat);
+ qDebug() << "backupExtensionFormat=" << _backupExtensionFormat;
+
+ _backupInterval = OctreePersistThread::DEFAULT_BACKUP_INTERVAL;
+ readOptionInt(QString("backupInterval"), settingsSectionObject, _backupInterval);
+ qDebug() << "backupInterval=" << _backupInterval;
+
+ _maxBackupVersions = OctreePersistThread::DEFAULT_MAX_BACKUP_VERSIONS;
+ readOptionInt(QString("maxBackupVersions"), settingsSectionObject, _maxBackupVersions);
+ qDebug() << "maxBackupVersions=" << _maxBackupVersions;
+ }
+
} else {
qDebug("persistFilename= DISABLED");
}
@@ -1112,7 +1139,9 @@ void OctreeServer::run() {
if (_wantPersist) {
// now set up PersistThread
- _persistThread = new OctreePersistThread(_tree, _persistFilename);
+ _persistThread = new OctreePersistThread(_tree, _persistFilename, _persistInterval,
+ _wantBackup, _backupInterval, _backupExtensionFormat,
+ _maxBackupVersions, _debugTimestampNow);
if (_persistThread) {
_persistThread->initialize(true);
}
@@ -1197,6 +1226,9 @@ void OctreeServer::aboutToFinish() {
qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node;
forceNodeShutdown(node);
}
+ if (_persistThread) {
+ _persistThread->aboutToFinish();
+ }
qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish...";
}
diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h
index b05896596c..27365c1e9d 100644
--- a/assignment-client/src/octree/OctreeServer.h
+++ b/assignment-client/src/octree/OctreeServer.h
@@ -162,11 +162,18 @@ protected:
bool _wantPersist;
bool _debugSending;
bool _debugReceiving;
+ bool _debugTimestampNow;
bool _verboseDebug;
JurisdictionMap* _jurisdiction;
JurisdictionSender* _jurisdictionSender;
OctreeInboundPacketProcessor* _octreeInboundPacketProcessor;
OctreePersistThread* _persistThread;
+
+ int _persistInterval;
+ bool _wantBackup;
+ QString _backupExtensionFormat;
+ int _backupInterval;
+ int _maxBackupVersions;
static OctreeServer* _instance;
diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index ba4cfe8dfd..50de26c518 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -167,7 +167,7 @@
"name": "reverb",
"type": "table",
"label": "Reverb Settings",
- "help": "In this table you can set custom reverb values for each audio zones",
+ "help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet level of -10 db. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet level of -5 db.",
"numbered": true,
"columns": [
{
@@ -276,6 +276,60 @@
"label": "Entity Server Settings",
"assignment-types": [6],
"settings": [
+ {
+ "name": "persistFilename",
+ "label": "Persistant Filename",
+ "help": "the filename for your entities",
+ "placeholder": "resources/models.svo",
+ "default": "resources/models.svo",
+ "advanced": true
+ },
+ {
+ "name": "persistInterval",
+ "label": "Persist Interval",
+ "help": "Interval between persist checks in msecs.",
+ "placeholder": "30000",
+ "default": "30000",
+ "advanced": true
+ },
+ {
+ "name": "NoPersist",
+ "type": "checkbox",
+ "help": "Don't persist your entities to a file.",
+ "default": false,
+ "advanced": true
+ },
+ {
+ "name": "backupExtensionFormat",
+ "label": "Backup File Extension Format:",
+ "help": "Format used to create the extension for the backup of your persisted entities. Use a format with %N to get rolling. Or use date formatting like %Y-%m-%d.%H:%M:%S.%z",
+ "placeholder": ".backup.%N",
+ "default": ".backup.%N",
+ "advanced": true
+ },
+ {
+ "name": "backupInterval",
+ "label": "Backup Interval",
+ "help": "Interval between backup checks in msecs.",
+ "placeholder": "1800000",
+ "default": "1800000",
+ "advanced": true
+ },
+ {
+ "name": "maxBackupVersions",
+ "label": "Max Rolled Backup Versions",
+ "help": "If your backup extension format uses 'rolling', how many versions do you want us to keep?",
+ "placeholder": "5",
+ "default": "5",
+ "advanced": true
+ },
+ {
+ "name": "NoBackup",
+ "type": "checkbox",
+ "help": "Don't regularly backup your persisted entities to a backup file.",
+ "default": false,
+ "advanced": true
+ },
{
"name": "statusHost",
"label": "Status Hostname",
@@ -313,6 +367,13 @@
"default": false,
"advanced": true
},
+ {
+ "name": "debugTimestampNow",
+ "type": "checkbox",
+ "help": "extra debugging for usecTimestampNow() function",
+ "default": false,
+ "advanced": true
+ },
{
"name": "clockSkew",
"label": "Clock Skew",
@@ -330,7 +391,52 @@
"label": "Voxel Server Settings",
"assignment-types": [3],
"settings": [
-
+ {
+ "name": "persistFilename",
+ "label": "Persistant Filename",
+ "help": "the filename for your voxels",
+ "placeholder": "resources/voxels.svo",
+ "default": "resources/voxels.svo",
+ "advanced": true
+ },
+ {
+ "name": "persistInterval",
+ "label": "Persist Interval",
+ "help": "Interval between persist checks in msecs.",
+ "placeholder": "30000",
+ "default": "30000",
+ "advanced": true
+ },
+ {
+ "name": "NoPersist",
+ "type": "checkbox",
+ "help": "Don't persist your voxels to a file.",
+ "default": false,
+ "advanced": true
+ },
+ {
+ "name": "backupExtensionFormat",
+ "label": "Backup File Extension Format:",
+ "help": "Format used to create the extension for the backup of your persisted voxels.",
+ "placeholder": ".backup.%Y-%m-%d.%H:%M:%S.%z",
+ "default": ".backup.%Y-%m-%d.%H:%M:%S.%z",
+ "advanced": true
+ },
+ {
+ "name": "backupInterval",
+ "label": "Backup Interval",
+ "help": "Interval between backup checks in msecs.",
+ "placeholder": "1800000",
+ "default": "1800000",
+ "advanced": true
+ },
+ {
+ "name": "NoBackup",
+ "type": "checkbox",
+ "help": "Don't regularly backup your persisted voxels to a backup file.",
+ "default": false,
+ "advanced": true
+ },
{
"name": "statusHost",
"label": "Status Hostname",
diff --git a/examples/cleanupChessboards.js b/examples/cleanupChessboards.js
new file mode 100644
index 0000000000..926c02b8cf
--- /dev/null
+++ b/examples/cleanupChessboards.js
@@ -0,0 +1,8 @@
+var entities = Entities.findEntities(MyAvatar.position, 10000);
+var URL = "https://s3.amazonaws.com/hifi-public/models/props/chess/";
+
+for(var i in entities) {
+ if (Entities.getEntityProperties(entities[i]).modelURL.slice(0, URL.length) === URL) {
+ Entities.deleteEntity(entities[i]);
+ }
+}
\ No newline at end of file
diff --git a/examples/clonedOverlaysExample.js b/examples/clonedOverlaysExample.js
new file mode 100644
index 0000000000..7aea048175
--- /dev/null
+++ b/examples/clonedOverlaysExample.js
@@ -0,0 +1,294 @@
+//
+// clonedOverlaysExample.js
+// examples
+//
+// Created by Thijs Wenker on 11/13/14.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Demonstrates the use of the overlay cloning function.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+const NUM_OF_TREES = 40;
+const NUM_OF_SANTAS = 20;
+
+// Image source: https://openclipart.org/detail/447/christmas-tree-by-theresaknott (heavily edited by Maximillian Merlin)
+const CHRISTMAS_TREE_SPRITES_URL = "http://test.thoys.nl/hifi/images/santa/christmas-tree.svg";
+
+// Image source: http://opengameart.org/content/santa-claus (CC-BY 3.0)
+const SANTA_SPRITES_URL = "http://test.thoys.nl/hifi/images/santa/Santa.png";
+
+Array.prototype.contains = function(obj) {
+ var i = this.length;
+ while (i--) {
+ if (this[i] === obj) {
+ return true;
+ }
+ }
+ return false;
+};
+
+function getRandomPosAroundMyAvatar(radius, height_offset) {
+ if (height_offset == undefined) {
+ height_offset = 0;
+ }
+ return {x: MyAvatar.position.x + Math.floor(Math.random() * radius * 2) - radius, y: MyAvatar.position.y + height_offset, z: MyAvatar.position.z + Math.floor(Math.random() * radius * 2) - radius};
+}
+
+function OverlayPreloader(overlay_type, overlay_properties, loaded_callback) {
+ var _this = this;
+ this.loaded_callback = loaded_callback;
+ this.overlay = Overlays.addOverlay(overlay_type, overlay_properties);
+ this.timer = null;
+ this.timer = Script.setInterval(function() {
+ if (Overlays.isLoaded(_this.overlay)) {
+ Script.clearInterval(_this.timer);
+ _this.loaded_callback();
+ }
+ }, 100);
+}
+
+function SpriteBillboard(sprite_properties, overlay) {
+ var _this = this;
+
+ // set properties
+ this.overlay = overlay;
+ this.overlay_properties = {};
+ this.sprite_properties = sprite_properties;
+ this.edited_overlay_properties = [];
+ this.defaultFPS = 30;
+ this.currentSequence = "";
+ this.sequenceIndex = 0;
+ this.sequenceFPS = 0;
+ this.sequenceStepToNext = false;
+ this.sequenceTimer = null;
+ this.prevSequenceIndex = -1;
+ this.sequenceX = 0;
+ this.sequenceY = 0;
+
+ this.spriteSize = {x: 0, y: 0, width: 32, height: 64};
+
+ this.editedProperty = function(prop_name) {
+ if (!_this.edited_overlay_properties.contains(prop_name)) {
+ _this.edited_overlay_properties.push(prop_name);
+ }
+ };
+
+ // function definitions
+ this.getPosition = function() {
+ return _this.overlay_properties.position ?
+ _this.overlay_properties.position : {x: 0, y: 0, z: 0};
+ };
+
+ this.setPosition = function(newPosition) {
+ this.overlay_properties.position = newPosition;
+ this.editedProperty("position");
+ return _this;
+ };
+
+ this.setAlpha = function(alpha) {
+ this.overlay_properties.alpha = alpha;
+ this.editedProperty("alpha");
+ return _this;
+ }
+
+ this.setScale = function(scale) {
+ this.overlay_properties.scale = scale;
+ this.editedProperty("scale");
+ return _this;
+ }
+
+ this.setSpriteSize = function(spriteSize) {
+ this.overlay_properties.subImage = spriteSize;
+ this.editedProperty("subImage");
+ return _this;
+ };
+
+ this.setSpriteXIndex = function(x_index) {
+ _this.sequenceX = x_index;
+ _this.overlay_properties.subImage.x = x_index * _this.overlay_properties.subImage.width;
+ _this.editedProperty("subImage");
+ return _this;
+ }
+
+ this.setSpriteYIndex = function(y_index) {
+ _this.sequenceY = y_index;
+ _this.overlay_properties.subImage.y = y_index * _this.overlay_properties.subImage.height;
+ _this.editedProperty("subImage");
+ return _this;
+ }
+
+ this.editOverlay = function(properties) {
+ for (var key in properties) {
+ _this.overlay_properties[attrname] = properties[key];
+ _this.editedProperty(key);
+ }
+ return _this;
+ };
+
+ this.commitChanges = function() {
+ var changed_properties = {};
+ for (var i = 0; i < _this.edited_overlay_properties.length; i++) {
+ var key = _this.edited_overlay_properties[i];
+ changed_properties[key] = _this.overlay_properties[key];
+ }
+ if (Object.keys(changed_properties).length === 0) {
+ return;
+ }
+ Overlays.editOverlay(_this.overlay, changed_properties);
+ _this.edited_overlay_properties = [];
+ };
+
+ this._renderFrame = function() {
+ var currentSequence = _this.sprite_properties.sequences[_this.currentSequence];
+ var currentItem = currentSequence[_this.sequenceIndex];
+ var indexChanged = _this.sequenceIndex != _this.prevSequenceIndex;
+ var canMoveToNext = true;
+ _this.prevSequenceIndex = _this.sequenceIndex;
+ if (indexChanged) {
+ if (currentItem.loop != undefined) {
+ _this.loopSequence = currentItem.loop;
+ }
+ if (currentItem.fps !== undefined && currentItem.fps != _this.sequenceFPS) {
+ _this.startSequenceTimerFPS(currentItem.fps);
+ }
+ if (currentItem.step_to_next !== undefined) {
+ _this.sequenceStepToNext = currentItem.step_to_next;
+ }
+ if (currentItem.x !== undefined) {
+ _this.setSpriteXIndex(currentItem.x);
+ }
+ if (currentItem.y !== undefined) {
+ _this.setSpriteYIndex(currentItem.y);
+ }
+
+ if (_this.sequenceStepToNext) {
+ canMoveToNext = false;
+ }
+ }
+ _this.prevSequenceIndex = _this.sequenceIndex;
+ var nextIndex = (_this.sequenceIndex + 1) % currentSequence.length;
+ var nextItem = currentSequence[nextIndex];
+ var nextX = nextItem.x != undefined ? nextItem.x : _this.sequenceX;
+ var nextY = nextItem.Y != undefined ? nextItem.Y : _this.sequenceY;
+
+ if (_this.sequenceStepToNext && !indexChanged) {
+ var XMoveNext = true;
+ var YMoveNext = true;
+ if (Math.abs(nextX - _this.sequenceX) > 1) {
+ _this.setSpriteXIndex(_this.sequenceX + (nextX > _this.sequenceX ? 1 : -1));
+ XMoveNext = Math.abs(nextX - _this.sequenceX) == 1;
+ }
+ if (Math.abs(nextY - _this.sequenceY) > 1) {
+ _this.setSpriteYIndex(_this.sequenceY + (nextY > _this.sequenceY ? 1 : -1));
+ YMoveNext = Math.abs(nextY - _this.sequenceY) == 1;
+ }
+ canMoveToNext = XMoveNext && YMoveNext;
+ }
+ if (canMoveToNext) {
+ _this.sequenceIndex = nextIndex;
+ }
+ _this.commitChanges();
+
+ };
+
+ this.clone = function() {
+ var clone = {};
+ clone.prototype = this.prototype;
+ for (property in this) {
+ clone[property] = this[property];
+ }
+ return clone;
+ };
+
+ this.startSequenceTimerFPS = function(fps) {
+ _this.sequenceFPS = fps;
+ if (_this.sequenceTimer != null) {
+ Script.clearInterval(_this.sequenceTimer);
+ }
+ _this.sequenceTimer = Script.setInterval(_this._renderFrame, 1000 / fps);
+ }
+
+ this.start = function(sequenceName) {
+ this.currentSequence = sequenceName;
+ this.sequenceFPS = this.defaultFPS;
+ this.startSequenceTimerFPS(this.defaultFPS);
+ };
+
+ if (this.sprite_properties.url !== undefined) {
+ this.setURL(this.sprite_properties.url);
+ }
+
+ if (this.sprite_properties.sprite_size !== undefined) {
+ this.setSpriteSize(this.sprite_properties.sprite_size);
+ }
+
+ if (this.sprite_properties.scale !== undefined) {
+ this.setScale(this.sprite_properties.scale);
+ }
+
+ if (this.sprite_properties.alpha !== undefined) {
+ this.setAlpha(this.sprite_properties.alpha);
+ }
+
+ if (this.sprite_properties.position !== undefined) {
+ this.setPosition(this.sprite_properties.position);
+ }
+
+ _this.commitChanges();
+
+ if (this.sprite_properties.startup_sequence != undefined) {
+ this.start(this.sprite_properties.startup_sequence);
+ }
+
+ Script.scriptEnding.connect(function () {
+ if (_this.sequenceTimer != null) {
+ Script.clearInterval(_this.sequenceTimer);
+ }
+ Overlays.deleteOverlay(_this.overlay);
+ });
+}
+
+var christmastree_loader = null;
+christmastree_loader = new OverlayPreloader("billboard",
+ {url: CHRISTMAS_TREE_SPRITES_URL, alpha: 0}, function() {
+ for (var i = 0; i < NUM_OF_TREES; i++) {
+ var clonedOverlay = Overlays.cloneOverlay(christmastree_loader.overlay);
+ new SpriteBillboard({
+ position: getRandomPosAroundMyAvatar(20),
+ sprite_size: {x: 0, y: 0, width: 250.000, height: 357.626},
+ scale: 6,
+ alpha: 1,
+ sequences: {"idle": [{x: 0, y: 0, step_to_next: true, fps: 3}, {x: 3, step_to_next: false}]},
+ startup_sequence: "idle"
+ }, clonedOverlay);
+ }
+ }
+);
+
+var santa_loader = null;
+santa_loader = new OverlayPreloader("billboard",
+ {url: SANTA_SPRITES_URL, alpha: 0}, function() {
+ for (var i = 0; i < NUM_OF_SANTAS; i++) {
+ var clonedOverlay = Overlays.cloneOverlay(santa_loader.overlay);
+ new SpriteBillboard({
+ position: getRandomPosAroundMyAvatar(18, -1),
+ sprite_size: {x: 0, y: 0, width: 64, height: 72},
+ scale: 4,
+ alpha: 1,
+ sequences: {
+ "walk_left": [{x: 2, y: 0, step_to_next: true, fps: 4}, {x: 10, step_to_next: false}],
+ "walk_right": [{x: 10, y: 1, step_to_next: true, fps: 4}, {x: 2, step_to_next: false}],
+ },
+ startup_sequence: (i % 2 == 0) ? "walk_left" : "walk_right"
+ }, clonedOverlay);
+ }
+ }
+);
+
+Script.scriptEnding.connect(function () {
+ Overlays.deleteOverlay(christmastree_loader.overlay);
+ Overlays.deleteOverlay(santa_loader.overlay);
+});
diff --git a/examples/editModels.js b/examples/editModels.js
index 7538a83fef..3f1863fef2 100644
--- a/examples/editModels.js
+++ b/examples/editModels.js
@@ -48,6 +48,9 @@ var RIGHT = 1;
var SPAWN_DISTANCE = 1;
var DEFAULT_DIMENSION = 0.20;
+var DEFAULT_TEXT_DIMENSION_X = 1.0;
+var DEFAULT_TEXT_DIMENSION_Y = 1.0;
+var DEFAULT_TEXT_DIMENSION_Z = 0.01;
var modelURLs = [
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Alder.fbx",
@@ -1122,6 +1125,7 @@ var toolBar = (function () {
newModelButton,
newCubeButton,
newSphereButton,
+ newTextButton,
browseModelsButton,
loadURLMenuItem,
loadFileMenuItem,
@@ -1208,7 +1212,16 @@ var toolBar = (function () {
alpha: 0.9,
visible: true
});
-
+
+ newTextButton = toolBar.addTool({
+ //imageURL: toolIconUrl + "add-text.svg",
+ imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/add-text.svg", // temporarily
+ subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
+ width: toolWidth,
+ height: toolHeight,
+ alpha: 0.9,
+ visible: true
+ });
}
function toggleNewModelButton(active) {
@@ -1372,6 +1385,25 @@ var toolBar = (function () {
}
+ if (newTextButton === toolBar.clicked(clickedOverlay)) {
+ var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
+
+ if (position.x > 0 && position.y > 0 && position.z > 0) {
+ Entities.addEntity({
+ type: "Text",
+ position: position,
+ dimensions: { x: DEFAULT_TEXT_DIMENSION_X, y: DEFAULT_TEXT_DIMENSION_Y, z: DEFAULT_TEXT_DIMENSION_Z },
+ backgroundColor: { red: 0, green: 0, blue: 0 },
+ textColor: { red: 255, green: 255, blue: 255 },
+ text: "some text",
+ lineHight: "0.1"
+ });
+ } else {
+ print("Can't create box: Text would be out of bounds.");
+ }
+ return true;
+ }
+
return false;
};
@@ -2447,7 +2479,7 @@ function Tooltip() {
margin: this.margin,
text: "",
color: { red: 228, green: 228, blue: 228 },
- alpha: 0.5,
+ alpha: 0.8,
visible: false
});
this.show = function (doShow) {
diff --git a/examples/entityScripts/chessPiece.js b/examples/entityScripts/chessPiece.js
new file mode 100644
index 0000000000..4d3fc5cc3d
--- /dev/null
+++ b/examples/entityScripts/chessPiece.js
@@ -0,0 +1,201 @@
+(function(){
+ this.FIRST_TILE = null; // Global position of the first tile (1a)
+ this.TILE_SIZE = null; // Size of one tile
+
+ this.wantDebug = false;
+ this.active = false;
+
+ this.entityID = null;
+ this.properties = null;
+ this.boardID = null;
+ this.boardUserData = null;
+
+ this.sound = null;
+ this.startingTile = null;
+ this.pieces = new Array();
+
+ // All callbacks start by updating the properties
+ this.updateProperties = function(entityID) {
+ // Piece ID
+ if (this.entityID === null || !this.entityID.isKnownID) {
+ this.entityID = Entities.identifyEntity(entityID);
+ }
+ // Piece Properties
+ this.properties = Entities.getEntityProperties(this.entityID);
+
+ // Board ID
+ if (this.boardID === null) {
+ // Read user data string and update boardID
+ var userData = JSON.parse(this.properties.userData);
+ var boardID = Entities.identifyEntity(userData.boardID);
+ if (boardID.isKnownID) {
+ this.boardID = boardID;
+ } else {
+ return;
+ }
+ }
+
+ // Board User Data
+ this.updateUserData();
+ }
+ // Get an entity's user data
+ this.getEntityUserData = function(entityID) {
+ var properties = Entities.getEntityProperties(entityID);
+
+ if (properties && properties.userData){
+ return JSON.parse(properties.userData);
+ } else {
+ print("No user data found.");
+ return null;
+ }
+ }
+ // Updates user data related objects
+ this.updateUserData = function() {
+ // Get board's user data
+ if (this.boardID !== null && this.boardID.isKnownID) {
+ this.boardUserData = this.getEntityUserData(this.boardID);
+
+ if (!(this.boardUserData &&
+ this.boardUserData.firstTile &&
+ this.boardUserData.tileSize)) {
+ print("Incomplete boardUserData " + this.boardID.id);
+ } else {
+ this.FIRST_TILE = this.boardUserData.firstTile;
+ this.TILE_SIZE = this.boardUserData.tileSize;
+
+ this.active = true;
+ }
+ } else {
+ print("Missing boardID:" + JSON.stringify(this.boardID));
+ }
+ }
+ // Returns whether pos is inside the grid or not
+ this.isOutsideGrid = function(pos) {
+ return !(pos.i >= 0 && pos.j >= 0 && pos.i < 8 && pos.j < 8);
+ }
+ // Returns the tile coordinate
+ this.getIndexPosition = function(position) {
+ var halfTile = this.TILE_SIZE / 2.0;
+ var corner = Vec3.sum(this.FIRST_TILE, { x: -halfTile, y: 0.0, z: -halfTile });
+ var relative = Vec3.subtract(position, corner);
+ return {
+ i: Math.floor(relative.x / this.TILE_SIZE),
+ j: Math.floor(relative.z / this.TILE_SIZE)
+ }
+ }
+ // Returns the world position
+ this.getAbsolutePosition = function(pos, halfHeight) {
+ var relative = {
+ x: pos.i * this.TILE_SIZE,
+ y: halfHeight,
+ z: pos.j * this.TILE_SIZE
+ }
+ return Vec3.sum(this.FIRST_TILE, relative);
+ }
+ // Pr, Vr are respectively the Ray's Point of origin and Vector director
+ // Pp, Np are respectively the Plane's Point of origin and Normal vector
+ this.rayPlaneIntersection = function(Pr, Vr, Pp, Np) {
+ var d = -Vec3.dot(Pp, Np);
+ var t = -(Vec3.dot(Pr, Np) + d) / Vec3.dot(Vr, Np);
+ return Vec3.sum(Pr, Vec3.multiply(t, Vr));
+ }
+ // Download sound if needed
+ this.maybeDownloadSound = function() {
+ if (this.sound === null) {
+ this.sound = SoundCache.getSound("http://public.highfidelity.io/sounds/Footsteps/FootstepW3Left-12db.wav");
+ }
+ }
+ // Play drop sound
+ this.playSound = function() {
+ if (this.sound && this.sound.downloaded) {
+ Audio.playSound(this.sound, { position: this.properties.position });
+ }
+ }
+ // updates the piece position based on mouse input
+ this.updatePosition = function(mouseEvent) {
+ var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y)
+ var upVector = { x: 0, y: 1, z: 0 };
+ var intersection = this.rayPlaneIntersection(pickRay.origin, pickRay.direction,
+ this.properties.position, upVector);
+ // if piece outside grid then send it back to the starting tile
+ if (this.isOutsideGrid(this.getIndexPosition(intersection))) {
+ intersection = this.getAbsolutePosition(this.startingTile, this.properties.dimensions.y / 2.0);
+ }
+ Entities.editEntity(this.entityID, { position: intersection });
+ }
+ // Snap piece to the center of the tile it is on
+ this.snapToGrid = function() {
+ var pos = this.getIndexPosition(this.properties.position);
+ var finalPos = this.getAbsolutePosition((this.isOutsideGrid(pos)) ? this.startingTile : pos,
+ this.properties.dimensions.y / 2.0);
+ Entities.editEntity(this.entityID, { position: finalPos });
+ }
+ this.moveDeadPiece = function() {
+ var myPos = this.getIndexPosition(this.properties.position);
+ var others = Entities.findEntities(this.properties.position, this.properties.dimensions.y);
+
+ for (var i = 0; i < others.length; i++) {
+ var piece = others[i];
+
+ if (piece.id != this.entityID.id) {
+ var properties = Entities.getEntityProperties(piece);
+
+ var isWhite = properties.modelURL.search("White") !== -1;
+ var type = (properties.modelURL.search("King") !== -1) ? 4 :
+ (properties.modelURL.search("Queen") !== -1) ? 3 :
+ (properties.modelURL.search("Rook") !== -1) ? 2 :
+ (properties.modelURL.search("Knight") !== -1) ? 1 :
+ (properties.modelURL.search("Bishop") !== -1) ? 0 :
+ (properties.modelURL.search("Pawn") !== -1) ? -1 : -2;
+
+ var piecePos = this.getIndexPosition(properties.position);
+ if (myPos.i === piecePos.i && myPos.j === piecePos.j && type !== -2) {
+ var position = this.getAbsolutePosition((isWhite) ? { i: type, j: -1 } : { i: 7 - type, j: 8 },
+ properties.dimensions.y / 2.0);
+ Entities.editEntity(piece, {
+ position: position
+ });
+ break;
+ }
+ }
+ }
+ }
+
+ this.grab = function(mouseEvent) {
+ if (this.active) {
+ this.startingTile = this.getIndexPosition(this.properties.position);
+ this.updatePosition(mouseEvent);
+ }
+ }
+ this.move = function(mouseEvent) {
+ if (this.active) {
+ this.updatePosition(mouseEvent);
+ }
+ }
+ this.release = function(mouseEvent) {
+ if (this.active) {
+ this.updatePosition(mouseEvent);
+ this.snapToGrid();
+ this.moveDeadPiece();
+ this.playSound();
+ }
+ }
+
+ this.preload = function(entityID) {
+ this.updateProperties(entityID); // All callbacks start by updating the properties
+ this.maybeDownloadSound();
+ }
+ this.clickDownOnEntity = function(entityID, mouseEvent) {
+ this.preload(entityID); // TODO : remove
+ this.updateProperties(entityID); // All callbacks start by updating the properties
+ this.grab(mouseEvent);
+ };
+ this.holdingClickOnEntity = function(entityID, mouseEvent) {
+ this.updateProperties(entityID); // All callbacks start by updating the properties
+ this.move(mouseEvent);
+ };
+ this.clickReleaseOnEntity = function(entityID, mouseEvent) {
+ this.updateProperties(entityID); // All callbacks start by updating the properties
+ this.release(mouseEvent);
+ };
+})
\ No newline at end of file
diff --git a/examples/entityScripts/movable.js b/examples/entityScripts/movable.js
new file mode 100644
index 0000000000..94ed7137fe
--- /dev/null
+++ b/examples/entityScripts/movable.js
@@ -0,0 +1,322 @@
+//
+// movable.js
+// examples/entityScripts
+//
+// Created by Brad Hefta-Gaub on 11/17/14.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+(function(){
+
+ this.entityID = null;
+ this.properties = null;
+ this.graboffset = null;
+ this.clickedAt = null;
+ this.firstHolding = true;
+ this.clickedX = -1;
+ this.clickedY = -1;
+ this.rotateOverlayTarget = null;
+ this.rotateOverlayInner = null;
+ this.rotateOverlayOuter = null;
+ this.rotateOverlayCurrent = null;
+ this.rotateMode = false;
+ this.originalRotation = null;
+ this.sound = null;
+ this.injector = null;
+
+ var rotateOverlayTargetSize = 10000; // really big target
+ var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool
+ var innerRadius;
+ var outerRadius;
+ var yawCenter;
+ var yawZero;
+ var rotationNormal;
+ var yawNormal;
+
+ var debug = true;
+
+ // Download sound if needed
+ this.maybeDownloadSound = function() {
+ if (this.sound === null) {
+ this.sound = SoundCache.getSound("http://public.highfidelity.io/sounds/Collisions-otherorganic/whoosh2.raw");
+ }
+ }
+ // Play drag sound
+ this.playSound = function() {
+ this.stopSound();
+ if (this.sound && this.sound.downloaded) {
+ this.injector = Audio.playSound(this.sound, { position: this.properties.position, loop: true, volume: 0.1 });
+ }
+ }
+
+ // stop drag sound
+ this.stopSound = function() {
+ if (this.injector) {
+ Audio.stopInjector(this.injector);
+ this.injector = null;
+ }
+ }
+
+ // Pr, Vr are respectively the Ray's Point of origin and Vector director
+ // Pp, Np are respectively the Plane's Point of origin and Normal vector
+ this.rayPlaneIntersection = function(Pr, Vr, Pp, Np) {
+ var d = -Vec3.dot(Pp, Np);
+ var t = -(Vec3.dot(Pr, Np) + d) / Vec3.dot(Vr, Np);
+ return Vec3.sum(Pr, Vec3.multiply(t, Vr));
+ };
+
+ // updates the piece position based on mouse input
+ this.updatePosition = function(mouseEvent) {
+ var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y)
+ var upVector = { x: 0, y: 1, z: 0 };
+ var intersection = this.rayPlaneIntersection(pickRay.origin, pickRay.direction,
+ this.properties.position, upVector);
+
+ var newPosition = Vec3.sum(intersection, this.graboffset);
+ Entities.editEntity(this.entityID, { position: newPosition });
+ };
+
+ this.grab = function(mouseEvent) {
+ // first calculate the offset
+ var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y)
+ var upVector = { x: 0, y: 1, z: 0 };
+ var intersection = this.rayPlaneIntersection(pickRay.origin, pickRay.direction,
+ this.properties.position, upVector);
+ this.graboffset = Vec3.subtract(this.properties.position, intersection);
+ };
+
+ this.move = function(mouseEvent) {
+ this.updatePosition(mouseEvent);
+ if (this.injector === null) {
+ this.playSound();
+ }
+ };
+
+ this.release = function(mouseEvent) {
+ this.updatePosition(mouseEvent);
+ };
+
+ this.rotate = function(mouseEvent) {
+ var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y)
+ var result = Overlays.findRayIntersection(pickRay);
+
+ if (result.intersects) {
+ var center = yawCenter;
+ var zero = yawZero;
+ var centerToZero = Vec3.subtract(center, zero);
+ var centerToIntersect = Vec3.subtract(center, result.intersection);
+ var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal);
+
+ var distanceFromCenter = Vec3.distance(center, result.intersection);
+ var snapToInner = false;
+ // var innerRadius = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1;
+ if (distanceFromCenter < innerRadius) {
+ angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle;
+ snapToInner = true;
+ }
+
+ var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 });
+ Entities.editEntity(this.entityID, { rotation: Quat.multiply(yawChange, this.originalRotation) });
+
+
+ // update the rotation display accordingly...
+ var startAtCurrent = 360-angleFromZero;
+ var endAtCurrent = 360;
+ var startAtRemainder = 0;
+ var endAtRemainder = 360-angleFromZero;
+ if (angleFromZero < 0) {
+ startAtCurrent = 0;
+ endAtCurrent = -angleFromZero;
+ startAtRemainder = -angleFromZero;
+ endAtRemainder = 360;
+ }
+
+ if (snapToInner) {
+ Overlays.editOverlay(this.rotateOverlayOuter, { startAt: 0, endAt: 360 });
+ Overlays.editOverlay(this.rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder });
+ Overlays.editOverlay(this.rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius,
+ majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0,
+ majorTickMarksLength: -0.25, minorTickMarksLength: 0, });
+ } else {
+ Overlays.editOverlay(this.rotateOverlayInner, { startAt: 0, endAt: 360 });
+ Overlays.editOverlay(this.rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder });
+ Overlays.editOverlay(this.rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius,
+ majorTickMarksAngle: 45.0, minorTickMarksAngle: 5,
+ majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, });
+ }
+ }
+ };
+ // All callbacks start by updating the properties
+ this.updateProperties = function(entityID) {
+ if (this.entityID === null || !this.entityID.isKnownID) {
+ this.entityID = Entities.identifyEntity(entityID);
+ }
+ this.properties = Entities.getEntityProperties(this.entityID);
+ };
+
+ this.cleanupRotateOverlay = function() {
+ Overlays.deleteOverlay(this.rotateOverlayTarget);
+ Overlays.deleteOverlay(this.rotateOverlayInner);
+ Overlays.deleteOverlay(this.rotateOverlayOuter);
+ Overlays.deleteOverlay(this.rotateOverlayCurrent);
+ this.rotateOverlayTarget = null;
+ this.rotateOverlayInner = null;
+ this.rotateOverlayOuter = null;
+ this.rotateOverlayCurrent = null;
+ }
+
+ this.displayRotateOverlay = function(mouseEvent) {
+ var yawOverlayAngles = { x: 90, y: 0, z: 0 };
+ var yawOverlayRotation = Quat.fromVec3Degrees(yawOverlayAngles);
+
+ yawNormal = { x: 0, y: 1, z: 0 };
+ yawCenter = this.properties.position;
+ rotationNormal = yawNormal;
+
+ // Size the overlays to the current selection size
+ var diagonal = (Vec3.length(this.properties.dimensions) / 2) * 1.1;
+ var halfDimensions = Vec3.multiply(this.properties.dimensions, 0.5);
+ innerRadius = diagonal;
+ outerRadius = diagonal * 1.15;
+ var innerAlpha = 0.2;
+ var outerAlpha = 0.2;
+
+ this.rotateOverlayTarget = Overlays.addOverlay("circle3d", {
+ position: this.properties.position,
+ size: 10000,
+ color: { red: 0, green: 0, blue: 0 },
+ alpha: 0.0,
+ solid: true,
+ visible: true,
+ rotation: yawOverlayRotation,
+ ignoreRayIntersection: false
+ });
+
+ this.rotateOverlayInner = Overlays.addOverlay("circle3d", {
+ position: this.properties.position,
+ size: innerRadius,
+ innerRadius: 0.9,
+ alpha: innerAlpha,
+ color: { red: 51, green: 152, blue: 203 },
+ solid: true,
+ visible: true,
+ rotation: yawOverlayRotation,
+ hasTickMarks: true,
+ majorTickMarksAngle: innerSnapAngle,
+ minorTickMarksAngle: 0,
+ majorTickMarksLength: -0.25,
+ minorTickMarksLength: 0,
+ majorTickMarksColor: { red: 0, green: 0, blue: 0 },
+ minorTickMarksColor: { red: 0, green: 0, blue: 0 },
+ ignoreRayIntersection: true, // always ignore this
+ });
+
+ this.rotateOverlayOuter = Overlays.addOverlay("circle3d", {
+ position: this.properties.position,
+ size: outerRadius,
+ innerRadius: 0.9,
+ startAt: 0,
+ endAt: 360,
+ alpha: outerAlpha,
+ color: { red: 51, green: 152, blue: 203 },
+ solid: true,
+ visible: true,
+ rotation: yawOverlayRotation,
+
+ hasTickMarks: true,
+ majorTickMarksAngle: 45.0,
+ minorTickMarksAngle: 5,
+ majorTickMarksLength: 0.25,
+ minorTickMarksLength: 0.1,
+ majorTickMarksColor: { red: 0, green: 0, blue: 0 },
+ minorTickMarksColor: { red: 0, green: 0, blue: 0 },
+ ignoreRayIntersection: true, // always ignore this
+ });
+
+ this.rotateOverlayCurrent = Overlays.addOverlay("circle3d", {
+ position: this.properties.position,
+ size: outerRadius,
+ startAt: 0,
+ endAt: 0,
+ innerRadius: 0.9,
+ color: { red: 224, green: 67, blue: 36},
+ alpha: 0.8,
+ solid: true,
+ visible: true,
+ rotation: yawOverlayRotation,
+ ignoreRayIntersection: true, // always ignore this
+ hasTickMarks: true,
+ majorTickMarksColor: { red: 0, green: 0, blue: 0 },
+ minorTickMarksColor: { red: 0, green: 0, blue: 0 },
+ });
+
+ var pickRay = Camera.computePickRay(mouseEvent.x, mouseEvent.y)
+ var result = Overlays.findRayIntersection(pickRay);
+ yawZero = result.intersection;
+
+ };
+
+ this.preload = function(entityID) {
+ this.updateProperties(entityID); // All callbacks start by updating the properties
+ this.maybeDownloadSound();
+ };
+
+ this.clickDownOnEntity = function(entityID, mouseEvent) {
+ this.updateProperties(entityID); // All callbacks start by updating the properties
+ this.grab(mouseEvent);
+
+ var d = new Date();
+ this.clickedAt = d.getTime();
+ this.firstHolding = true;
+
+ this.clickedX = mouseEvent.x;
+ this.clickedY = mouseEvent.y;
+ };
+
+ this.holdingClickOnEntity = function(entityID, mouseEvent) {
+
+ this.updateProperties(entityID); // All callbacks start by updating the properties
+
+ if (this.firstHolding) {
+ // if we haven't moved yet...
+ if (this.clickedX == mouseEvent.x && this.clickedY == mouseEvent.y) {
+ var d = new Date();
+ var now = d.getTime();
+
+ if (now - this.clickedAt > 500) {
+ this.displayRotateOverlay(mouseEvent);
+ this.firstHolding = false;
+ this.rotateMode = true;
+ this.originalRotation = this.properties.rotation;
+ }
+ } else {
+ this.firstHolding = false;
+ }
+ }
+
+ if (this.rotateMode) {
+ this.rotate(mouseEvent);
+ } else {
+ this.move(mouseEvent);
+ }
+ };
+ this.clickReleaseOnEntity = function(entityID, mouseEvent) {
+ this.updateProperties(entityID); // All callbacks start by updating the properties
+ if (this.rotateMode) {
+ this.rotate(mouseEvent);
+ } else {
+ this.release(mouseEvent);
+ }
+
+ if (this.rotateOverlayTarget != null) {
+ this.cleanupRotateOverlay();
+ this.rotateMode = false;
+ }
+
+ this.firstHolding = false;
+ this.stopSound();
+ };
+
+})
diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index 408e83198f..695879b678 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -134,6 +134,17 @@
var elModelAnimationURL = document.getElementById("property-model-animation-url");
var elModelAnimationPlaying = document.getElementById("property-model-animation-playing");
var elModelAnimationFPS = document.getElementById("property-model-animation-fps");
+ var elModelAnimationFrame = document.getElementById("property-model-animation-frame");
+
+ var elTextSection = document.getElementById("text-section");
+ var elTextText = document.getElementById("property-text-text");
+ var elTextLineHeight = document.getElementById("property-text-line-height");
+ var elTextTextColorRed = document.getElementById("property-text-text-color-red");
+ var elTextTextColorGreen = document.getElementById("property-text-text-color-green");
+ var elTextTextColorBlue = document.getElementById("property-text-text-color-blue");
+ var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red");
+ var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green");
+ var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue");
if (window.EventBridge !== undefined) {
EventBridge.scriptEventReceived.connect(function(data) {
@@ -201,13 +212,27 @@
elModelSection.style.display = 'none';
} else {
elModelSection.style.display = 'block';
-
elModelURL.value = properties.modelURL;
elModelAnimationURL.value = properties.animationURL;
elModelAnimationPlaying.checked = properties.animationPlaying;
elModelAnimationFPS.value = properties.animationFPS;
}
+ if (properties.type != "Text") {
+ elTextSection.style.display = 'none';
+ } else {
+ elTextSection.style.display = 'block';
+
+ elTextText.value = properties.text;
+ elTextLineHeight.value = properties.lineHeight;
+ elTextTextColorRed.value = properties.textColor.red;
+ elTextTextColorGreen.value = properties.textColor.green;
+ elTextTextColorBlue.value = properties.textColor.blue;
+ elTextBackgroundColorRed.value = properties.backgroundColor.red;
+ elTextBackgroundColorGreen.value = properties.backgroundColor.green;
+ elTextBackgroundColorBlue.value = properties.backgroundColor.blue;
+ }
+
if (properties.type != "Light") {
elLightSection.style.display = 'none';
} else {
@@ -320,6 +345,22 @@
elModelAnimationPlaying.addEventListener('change', createEmitCheckedPropertyUpdateFunction('animationIsPlaying'));
elModelAnimationFPS.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFPS'));
elModelAnimationFrame.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFrameIndex'));
+
+ elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
+ elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight'));
+
+ var textTextColorChangeFunction = createEmitColorPropertyUpdateFunction(
+ 'textColor', elTextTextColorRed, elTextTextColorGreen, elTextTextColorBlue);
+ elTextTextColorRed.addEventListener('change', textTextColorChangeFunction);
+ elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction);
+ elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction);
+
+ var textBackgroundColorChangeFunction = createEmitColorPropertyUpdateFunction(
+ 'backgroundColor', elTextBackgroundColorRed, elTextBackgroundColorGreen, elTextBackgroundColorBlue);
+ elTextBackgroundColorRed.addEventListener('change', textBackgroundColorChangeFunction);
+ elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction);
+ elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
+
}
@@ -490,6 +531,43 @@
+
+
+
+
+
+
+
+
+
diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js
index da60e0c370..a162697560 100644
--- a/examples/libraries/entityPropertyDialogBox.js
+++ b/examples/libraries/entityPropertyDialogBox.js
@@ -41,15 +41,15 @@ EntityPropertyDialogBox = (function () {
array.push({ label: "Entity Type:" + properties.type, type: "header" });
index++;
- array.push({ label: "Locked:", value: properties.locked });
+ array.push({ label: "Locked:", type: "checkbox", value: properties.locked });
index++;
-
+
if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL });
index++;
array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
- array.push({ label: "Animation is playing:", value: properties.animationIsPlaying });
+ array.push({ label: "Animation is playing:", type: "checkbox", value: properties.animationIsPlaying });
previousAnimationIsPlaying = properties.animationIsPlaying;
index++;
array.push({ label: "Animation FPS:", value: properties.animationFPS });
@@ -65,6 +65,30 @@ EntityPropertyDialogBox = (function () {
array.push({ label: "Original Textures:\n" + properties.originalTextures, type: "header" });
index++;
}
+
+ if (properties.type == "Text") {
+ array.push({ label: "Text:", value: properties.text });
+ index++;
+ array.push({ label: "Line Height:", value: properties.lineHeight });
+ index++;
+ array.push({ label: "Text Color:", type: "header" });
+ index++;
+ array.push({ label: "Red:", value: properties.textColor.red });
+ index++;
+ array.push({ label: "Green:", value: properties.textColor.green });
+ index++;
+ array.push({ label: "Blue:", value: properties.textColor.blue });
+ index++;
+ array.push({ label: "Background Color:", type: "header" });
+ index++;
+ array.push({ label: "Red:", value: properties.backgroundColor.red });
+ index++;
+ array.push({ label: "Green:", value: properties.backgroundColor.green });
+ index++;
+ array.push({ label: "Blue:", value: properties.backgroundColor.blue });
+ index++;
+ }
+
array.push({ label: "Position:", type: "header" });
index++;
array.push({ label: "X:", value: properties.position.x.toFixed(decimals) });
@@ -140,20 +164,20 @@ EntityPropertyDialogBox = (function () {
index++;
array.push({ label: "Mass:", value: properties.mass.toFixed(decimals) });
index++;
- array.push({ label: "Ignore for Collisions:", value: properties.ignoreForCollisions });
+ array.push({ label: "Ignore for Collisions:", type: "checkbox", value: properties.ignoreForCollisions });
index++;
- array.push({ label: "Collisions Will Move:", value: properties.collisionsWillMove });
+ array.push({ label: "Collisions Will Move:", type: "checkbox", value: properties.collisionsWillMove });
index++;
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
index++;
- array.push({ label: "Visible:", value: properties.visible });
+ array.push({ label: "Visible:", type: "checkbox", value: properties.visible });
index++;
array.push({ label: "Script:", value: properties.script });
index++;
-
+
if (properties.type == "Box" || properties.type == "Sphere") {
array.push({ label: "Color:", type: "header" });
index++;
@@ -225,7 +249,7 @@ EntityPropertyDialogBox = (function () {
var rescaledX = peekX * peekRescale / 100.0;
var rescaledY = peekY * peekRescale / 100.0;
var rescaledZ = peekZ * peekRescale / 100.0;
-
+
Window.reloadNonBlockingForm([
{ value: rescaledX.toFixed(decimals), oldIndex: dimensionX },
{ value: rescaledY.toFixed(decimals), oldIndex: dimensionY },
@@ -271,6 +295,22 @@ EntityPropertyDialogBox = (function () {
properties.textures = array[index++].value;
index++; // skip textureNames label
}
+
+ if (properties.type == "Text") {
+ properties.text = array[index++].value;
+ properties.lineHeight = array[index++].value;
+
+ index++; // skip header
+ properties.textColor.red = array[index++].value;
+ properties.textColor.green = array[index++].value;
+ properties.textColor.blue = array[index++].value;
+
+ index++; // skip header
+ properties.backgroundColor.red = array[index++].value;
+ properties.backgroundColor.green = array[index++].value;
+ properties.backgroundColor.blue = array[index++].value;
+ }
+
index++; // skip header
properties.position.x = array[index++].value;
properties.position.y = array[index++].value;
@@ -354,4 +394,3 @@ EntityPropertyDialogBox = (function () {
return that;
}());
-
diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js
index cc3c0fceda..f866893411 100644
--- a/examples/newEditEntities.js
+++ b/examples/newEditEntities.js
@@ -54,6 +54,9 @@ var wantEntityGlow = false;
var SPAWN_DISTANCE = 1;
var DEFAULT_DIMENSION = 0.20;
+var DEFAULT_TEXT_DIMENSION_X = 1.0;
+var DEFAULT_TEXT_DIMENSION_Y = 1.0;
+var DEFAULT_TEXT_DIMENSION_Z = 0.01;
var MENU_INSPECT_TOOL_ENABLED = "Inspect Tool";
var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus";
@@ -84,6 +87,7 @@ var toolBar = (function () {
newCubeButton,
newSphereButton,
newLightButton,
+ newTextButton,
browseModelsButton,
loadURLMenuItem,
loadFileMenuItem,
@@ -180,6 +184,16 @@ var toolBar = (function () {
visible: true
});
+ newTextButton = toolBar.addTool({
+ //imageURL: toolIconUrl + "add-text.svg",
+ imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/add-text.svg", // temporarily
+ subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
+ width: toolWidth,
+ height: toolHeight,
+ alpha: 0.9,
+ visible: true
+ });
+
}
function toggleNewModelButton(active) {
@@ -357,7 +371,7 @@ var toolBar = (function () {
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
if (position.x > 0 && position.y > 0 && position.z > 0) {
- Entities.addEntity({
+ Entities.addEntity({
type: "Light",
position: position,
dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
@@ -378,6 +392,24 @@ var toolBar = (function () {
return true;
}
+ if (newTextButton === toolBar.clicked(clickedOverlay)) {
+ var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
+
+ if (position.x > 0 && position.y > 0 && position.z > 0) {
+ Entities.addEntity({
+ type: "Text",
+ position: position,
+ dimensions: { x: DEFAULT_TEXT_DIMENSION_X, y: DEFAULT_TEXT_DIMENSION_Y, z: DEFAULT_TEXT_DIMENSION_Z },
+ backgroundColor: { red: 0, green: 0, blue: 0 },
+ textColor: { red: 255, green: 255, blue: 255 },
+ text: "some text",
+ lineHight: "0.1"
+ });
+ } else {
+ print("Can't create box: Text would be out of bounds.");
+ }
+ return true;
+ }
return false;
@@ -710,7 +742,7 @@ function handeMenuEvent(menuItem) {
var selectedModel = form[0].value;
if (form[1].value == "Properties") {
editModelID = selectedModel;
- showPropertiesForm(editModelID);
+ entityPropertyDialogBox.openDialog(editModelID);
} else if (form[1].value == "Delete") {
Entities.deleteEntity(selectedModel);
} else if (form[1].value == "Teleport") {
diff --git a/examples/playChess.js b/examples/playChess.js
new file mode 100644
index 0000000000..57ed24f342
--- /dev/null
+++ b/examples/playChess.js
@@ -0,0 +1,262 @@
+//
+// playChess.js
+// examples
+//
+// Created by Clement Brisset on 11/08/2014
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+var gamePosition = { x: 1.0, y: 1.0, z: 1.0 };
+var gameSize = 1.0;
+
+// Script namespace
+var extraDebug = extraDebug || true;
+var ChessGame = ChessGame || {
+ BOARD: {
+ modelURL: "https://s3.amazonaws.com/hifi-public/models/props/chess/Board.fbx",
+ dimensions: { x: 773.191, y: 20.010, z: 773.191 },
+ rotation: Quat.fromPitchYawRollDegrees(0, 90, 0),
+ numTiles: 10.0
+ },
+ KING: 0, QUEEN: 1, BISHOP: 2, KNIGHT: 3, ROOK: 4, PAWN: 5,
+
+ board: null,
+ pieces: new Array()
+};
+
+ChessGame.getPieceInfo = function(type, isWhite) {
+ var info = {
+ modelURL: "https://s3.amazonaws.com/hifi-public/models/props/chess/",
+ dimensions: { x: 1.0, y: 1.0, z: 1.0 },
+ rotation: Quat.fromPitchYawRollDegrees(0, 0, 0)
+ }
+
+ switch(type) {
+ case ChessGame.KING:
+ info.modelURL += "King";
+ info.dimensions = { x: 110.496, y: 306.713, z: 110.496 };
+ info.rotation = Quat.fromPitchYawRollDegrees(0, 90, 0);
+ break;
+ case ChessGame.QUEEN:
+ info.modelURL += "Queen";
+ info.dimensions = { x: 110.496, y: 257.459, z: 110.496 };
+ break;
+ case ChessGame.BISHOP:
+ info.modelURL += "Bishop";
+ info.dimensions = { x: 102.002, y: 213.941, z: 102.002 };
+ info.rotation = Quat.fromPitchYawRollDegrees(0, 90, 0);
+ break;
+ case ChessGame.KNIGHT:
+ info.modelURL += "Knight";
+ info.dimensions = { x: 95.602, y: 186.939, z: 95.602 };
+ info.rotation = Quat.fromPitchYawRollDegrees(0, 180, 0);
+ break;
+ case ChessGame.ROOK:
+ info.modelURL += "Rook";
+ info.dimensions = { x: 101.024, y: 167.523, z: 101.024 };
+ break;
+ case ChessGame.PAWN:
+ info.modelURL += "Pawn";
+ info.dimensions = { x: 93.317, y: 138.747, z: 93.317 };
+ break;
+ }
+ info.modelURL += ((isWhite) ? "_White" : "_Black") + ".fbx"
+
+ return info;
+}
+
+
+// Board class
+ChessGame.Board = (function(position, scale) {
+ this.dimensions = Vec3.multiply(1.0 / ChessGame.BOARD.dimensions.x, ChessGame.BOARD.dimensions); // 1 by 1
+ this.dimensions = Vec3.multiply(scale, this.dimensions); // scale by scale
+ this.position = position
+ this.tileSize = scale / ChessGame.BOARD.numTiles;
+
+ this.userDataObject = {
+ firstTile: this.tilePosition(1, 1),
+ tileSize: this.tileSize,
+ whitesDeadPieces: this.tilePosition(0,0),
+ blacksDeadPieces: this.tilePosition(9,9)
+ }
+ this.entityProperties = {
+ type: "Model",
+ modelURL: ChessGame.BOARD.modelURL,
+ position: this.position,
+ dimensions: this.dimensions,
+ userData: this.buildUserDataString()
+ }
+ this.entity = null;
+});
+// Returns the top center point of tile i,j
+ChessGame.Board.prototype.tilePosition = function(i, j) {
+ if (!(this.position || this.dimensions || this.tileSize || ChessGame.BOARD.numTiles)) {
+ print("ChessGame.Board.prototype.tilePosition(): Called before proper initialisation, bailing.");
+ return null;
+ }
+ return {
+ x: this.position.x + (i - ChessGame.BOARD.numTiles / 2.0 + 0.5) * this.tileSize,
+ y: this.position.y + this.dimensions.y / 2.0,
+ z: this.position.z + (j - ChessGame.BOARD.numTiles / 2.0 + 0.5) * this.tileSize
+ };
+}
+// Checks the color of the tile
+ChessGame.Board.prototype.isWhite = function(i, j) {
+ return (i + j) % 2 != 0;
+}
+// Build the user data string
+ChessGame.Board.prototype.buildUserDataString = function() {
+ return JSON.stringify(this.userDataObject);
+}
+// Updates the user data stored by the board
+ChessGame.Board.prototype.updateUserData = function() {
+ var userDataString = this.buildUserDataString();
+ this.entityProperties.userData = userDataString;
+ Entities.editEntity(this.entity, { userData: userDataString });
+}
+// Spawns the board using entities
+ChessGame.Board.prototype.spawn = function() {
+ if (extraDebug) {
+ print("Spawning board...");
+ }
+ this.entity = Entities.addEntity(this.entityProperties);
+ print("Board spawned");
+}
+// Cleans up the entities of the board
+ChessGame.Board.prototype.cleanup = function() {
+ if (extraDebug) {
+ print("Cleaning up board...");
+ }
+ Entities.deleteEntity(this.entity);
+ print("Board cleaned up");
+}
+
+// Piece class
+ChessGame.Piece = (function(position, dimensions, url, rotation) {
+ this.dimensions = dimensions;
+ this.position = position;
+ this.position.y += this.dimensions.y / 2.0;
+
+ this.entityProperties = {
+ type: "Model",
+ position: this.position,
+ rotation: rotation,
+ dimensions: this.dimensions,
+ modelURL: url,
+ /**/
+ script: "https://s3.amazonaws.com/hifi-public/scripts/entityScripts/chessPiece.js",
+ /*/
+ script: "file:/Users/clement/hifi/examples/entityScripts/chessPiece.js",
+ /**/
+ userData: this.buildUserDataString()
+ }
+ this.entity = null;
+});
+// Build the user data string
+ChessGame.Piece.prototype.buildUserDataString = function() {
+ if (!(ChessGame.board !== null || ChessGame.board.entity.isKnownID)) {
+ print("ChessGame.Piece.prototype.buildUserDataString(): Called before proper initialization, bailing.");
+ return null;
+ }
+ var userDataObject = {
+ boardID: ChessGame.board.entity,
+ };
+ return JSON.stringify(userDataObject);
+}
+// Spawns the piece
+ChessGame.Piece.prototype.spawn = function() {
+ this.entity = Entities.addEntity(this.entityProperties);
+}
+// Cleans up the piece
+ChessGame.Piece.prototype.cleanup = function() {
+ Entities.deleteEntity(this.entity);
+}
+ChessGame.makePiece = function(piece, i, j, isWhite) {
+ var info = ChessGame.getPieceInfo(piece, isWhite);
+ var url = info.modelURL;
+ var size = Vec3.multiply(0.5 * (1.0 / ChessGame.BOARD.dimensions.x), info.dimensions);
+ var rotation = (isWhite) ? info.rotation
+ : Quat.multiply(Quat.fromPitchYawRollDegrees(0, 180, 0),
+ info.rotation);
+ var position = ChessGame.board.tilePosition(i, j);
+
+ var piece = new ChessGame.Piece(position, size, url, rotation);
+ piece.spawn();
+ ChessGame.pieces.push(piece);
+ return piece;
+}
+
+ChessGame.scriptStarting = function() {
+ print("playChess.js started");
+ gamePosition = Vec3.sum(MyAvatar.position,
+ Vec3.multiplyQbyV(MyAvatar.orientation,
+ { x: 0, y: 0, z: -1 }));
+ // Setup board
+ ChessGame.board = new ChessGame.Board(gamePosition, gameSize);
+ ChessGame.board.spawn();
+}
+
+ChessGame.update = function() {
+ ChessGame.board.entity = Entities.identifyEntity(ChessGame.board.entity);
+
+ if (ChessGame.board.entity.isKnownID && ChessGame.pieces.length == 0) {
+ // Setup white pieces
+ var isWhite = true;
+ var row = 1;
+ // King
+ ChessGame.makePiece(ChessGame.KING, row, 5, isWhite);
+ // Queen
+ ChessGame.makePiece(ChessGame.QUEEN, row, 4, isWhite);
+ // Bishop
+ ChessGame.makePiece(ChessGame.BISHOP, row, 3, isWhite);
+ ChessGame.makePiece(ChessGame.BISHOP, row, 6, isWhite);
+ // Knight
+ ChessGame.makePiece(ChessGame.KNIGHT, row, 2, isWhite);
+ ChessGame.makePiece(ChessGame.KNIGHT, row, 7, isWhite);
+ // Rook
+ ChessGame.makePiece(ChessGame.ROOK, row, 1, isWhite);
+ ChessGame.makePiece(ChessGame.ROOK, row, 8, isWhite);
+ for(var j = 1; j <= 8; j++) {
+ ChessGame.makePiece(ChessGame.PAWN, row + 1, j, isWhite);
+ }
+
+ // Setup black pieces
+ isWhite = false;
+ row = 8;
+ // King
+ ChessGame.makePiece(ChessGame.KING, row, 5, isWhite);
+ // Queen
+ ChessGame.makePiece(ChessGame.QUEEN, row, 4, isWhite);
+ // Bishop
+ ChessGame.makePiece(ChessGame.BISHOP, row, 3, isWhite);
+ ChessGame.makePiece(ChessGame.BISHOP, row, 6, isWhite);
+ // Knight
+ ChessGame.makePiece(ChessGame.KNIGHT, row, 2, isWhite);
+ ChessGame.makePiece(ChessGame.KNIGHT, row, 7, isWhite);
+ // Rook
+ ChessGame.makePiece(ChessGame.ROOK, row, 1, isWhite);
+ ChessGame.makePiece(ChessGame.ROOK, row, 8, isWhite);
+ for(var j = 1; j <= 8; j++) {
+ ChessGame.makePiece(ChessGame.PAWN, row - 1, j, isWhite);
+ }
+ Script.update.disconnect(ChessGame.update);
+ }
+}
+
+ChessGame.scriptEnding = function() {
+ // Cleaning up board
+ ChessGame.board.cleanup();
+
+ // Cleaning up pieces
+ for(var i in ChessGame.pieces) {
+ ChessGame.pieces[i].cleanup();
+ }
+
+ print("playChess.js finished");
+}
+
+Script.scriptEnding.connect(ChessGame.scriptEnding);
+Script.update.connect(ChessGame.update);
+ChessGame.scriptStarting();
\ No newline at end of file
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index f890baff3d..31be016380 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1555,6 +1555,8 @@ void Application::checkBandwidthMeterClick() {
// ... to be called upon button release
if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth) &&
+ Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
+ Menu::getInstance()->isOptionChecked(MenuOption::UserInterface) &&
glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY)))
<= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH
&& _bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) {
diff --git a/interface/src/Application.h b/interface/src/Application.h
index dc1ec96321..2c70249b32 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -115,15 +115,15 @@ static const float NODE_KILLED_BLUE = 0.0f;
static const QString SNAPSHOT_EXTENSION = ".jpg";
static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees
-static const float BILLBOARD_DISTANCE = 5.0f; // meters
+static const float BILLBOARD_DISTANCE = 5.56f; // meters
static const int MIRROR_VIEW_TOP_PADDING = 5;
static const int MIRROR_VIEW_LEFT_PADDING = 10;
static const int MIRROR_VIEW_WIDTH = 265;
static const int MIRROR_VIEW_HEIGHT = 215;
-static const float MIRROR_FULLSCREEN_DISTANCE = 0.35f;
-static const float MIRROR_REARVIEW_DISTANCE = 0.65f;
-static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f;
+static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f;
+static const float MIRROR_REARVIEW_DISTANCE = 0.722f;
+static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f;
static const float MIRROR_FIELD_OF_VIEW = 30.0f;
static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND;
diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp
index 2dc94e6027..b7076b6c1a 100644
--- a/interface/src/Audio.cpp
+++ b/interface/src/Audio.cpp
@@ -99,6 +99,7 @@ Audio::Audio(QObject* parent) :
_muted(false),
_reverb(false),
_reverbOptions(&_scriptReverbOptions),
+ _gverbLocal(NULL),
_gverb(NULL),
_iconColor(1.0f),
_iconPulseTimeReference(usecTimestampNow()),
@@ -504,12 +505,23 @@ bool Audio::switchOutputToAudioDevice(const QString& outputDeviceName) {
void Audio::initGverb() {
// Initialize a new gverb instance
+ _gverbLocal = gverb_new(_outputFormat.sampleRate(), _reverbOptions->getMaxRoomSize(), _reverbOptions->getRoomSize(),
+ _reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
+ _reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
+ _reverbOptions->getTailLevel());
_gverb = gverb_new(_outputFormat.sampleRate(), _reverbOptions->getMaxRoomSize(), _reverbOptions->getRoomSize(),
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
_reverbOptions->getTailLevel());
-
+
// Configure the instance (these functions are not super well named - they actually set several internal variables)
+ gverb_set_roomsize(_gverbLocal, _reverbOptions->getRoomSize());
+ gverb_set_revtime(_gverbLocal, _reverbOptions->getReverbTime());
+ gverb_set_damping(_gverbLocal, _reverbOptions->getDamping());
+ gverb_set_inputbandwidth(_gverbLocal, _reverbOptions->getInputBandwidth());
+ gverb_set_earlylevel(_gverbLocal, DB_CO(_reverbOptions->getEarlyLevel()));
+ gverb_set_taillevel(_gverbLocal, DB_CO(_reverbOptions->getTailLevel()));
+
gverb_set_roomsize(_gverb, _reverbOptions->getRoomSize());
gverb_set_revtime(_gverb, _reverbOptions->getReverbTime());
gverb_set_damping(_gverb, _reverbOptions->getDamping());
@@ -565,25 +577,27 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) {
}
}
-void Audio::addReverb(int16_t* samplesData, int numSamples, QAudioFormat& audioFormat) {
- float dryFraction = DB_CO(_reverbOptions->getDryLevel());
+void Audio::addReverb(ty_gverb* gverb, int16_t* samplesData, int numSamples, QAudioFormat& audioFormat, bool noEcho) {
float wetFraction = DB_CO(_reverbOptions->getWetLevel());
+ float dryFraction = (noEcho) ? 0.0f : (1.0f - wetFraction);
float lValue,rValue;
for (int sample = 0; sample < numSamples; sample += audioFormat.channelCount()) {
// Run GVerb
float value = (float)samplesData[sample];
- gverb_do(_gverb, value, &lValue, &rValue);
+ gverb_do(gverb, value, &lValue, &rValue);
// Mix, accounting for clipping, the left and right channels. Ignore the rest.
for (int j = sample; j < sample + audioFormat.channelCount(); j++) {
if (j == sample) {
// left channel
- int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction), MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
+ int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction),
+ MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
samplesData[j] = (int16_t)lResult;
} else if (j == (sample + 1)) {
// right channel
- int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction), MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
+ int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction),
+ MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
samplesData[j] = (int16_t)rResult;
} else {
// ignore channels above 2
@@ -622,23 +636,10 @@ void Audio::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
}
if (hasLocalReverb) {
- QByteArray loopbackCopy;
- if (!hasEcho) {
- loopbackCopy = loopBackByteArray;
- }
-
int16_t* loopbackSamples = reinterpret_cast(loopBackByteArray.data());
int numLoopbackSamples = loopBackByteArray.size() / sizeof(int16_t);
updateGverbOptions();
- addReverb(loopbackSamples, numLoopbackSamples, _outputFormat);
-
- if (!hasEcho) {
- int16_t* loopbackCopySamples = reinterpret_cast(loopbackCopy.data());
- for (int i = 0; i < numLoopbackSamples; ++i) {
- loopbackSamples[i] = glm::clamp((int)loopbackSamples[i] - loopbackCopySamples[i],
- MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
- }
- }
+ addReverb(_gverbLocal, loopbackSamples, numLoopbackSamples, _outputFormat, !hasEcho);
}
if (_loopbackOutputDevice) {
@@ -1029,7 +1030,7 @@ void Audio::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& ou
if(_reverb || _receivedAudioStream.hasReverb()) {
updateGverbOptions();
- addReverb((int16_t*)outputBuffer.data(), numDeviceOutputSamples, _outputFormat);
+ addReverb(_gverb, (int16_t*)outputBuffer.data(), numDeviceOutputSamples, _outputFormat);
}
}
diff --git a/interface/src/Audio.h b/interface/src/Audio.h
index d8e5c5386c..127c2e3332 100644
--- a/interface/src/Audio.h
+++ b/interface/src/Audio.h
@@ -248,6 +248,7 @@ private:
AudioEffectOptions _scriptReverbOptions;
AudioEffectOptions _zoneReverbOptions;
AudioEffectOptions* _reverbOptions;
+ ty_gverb* _gverbLocal;
ty_gverb* _gverb;
GLuint _micTextureId;
GLuint _muteTextureId;
@@ -269,7 +270,7 @@ private:
// Adds Reverb
void initGverb();
void updateGverbOptions();
- void addReverb(int16_t* samples, int numSamples, QAudioFormat& format);
+ void addReverb(ty_gverb* gverb, int16_t* samples, int numSamples, QAudioFormat& format, bool noEcho = false);
void handleLocalEchoAndReverb(QByteArray& inputByteArray);
diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp
index 6f9f4cae68..96680423ed 100644
--- a/interface/src/DatagramProcessor.cpp
+++ b/interface/src/DatagramProcessor.cpp
@@ -141,17 +141,29 @@ void DatagramProcessor::processDatagrams() {
AccountManager::getInstance().checkAndSignalForAccessToken();
break;
}
+ case PacketTypeNoisyMute:
case PacketTypeMuteEnvironment: {
- glm::vec3 position;
- float radius;
+ bool mute = !Application::getInstance()->getAudio()->getMuted();
- int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
- memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3));
- memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float));
+ if (incomingType == PacketTypeMuteEnvironment) {
+ glm::vec3 position;
+ float radius, distance;
+
+ int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
+ memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3));
+ memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float));
+ distance = glm::distance(Application::getInstance()->getAvatar()->getPosition(), position);
+
+ mute = mute && (distance < radius);
+ }
- if (glm::distance(Application::getInstance()->getAvatar()->getPosition(), position) < radius
- && !Application::getInstance()->getAudio()->getMuted()) {
+ if (mute) {
Application::getInstance()->getAudio()->toggleMute();
+ if (incomingType == PacketTypeMuteEnvironment) {
+ AudioScriptingInterface::getInstance().environmentMuted();
+ } else {
+ AudioScriptingInterface::getInstance().mutedByMixer();
+ }
}
break;
}
diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp
index 2209a992c1..016b7b5363 100644
--- a/interface/src/entities/EntityTreeRenderer.cpp
+++ b/interface/src/entities/EntityTreeRenderer.cpp
@@ -34,6 +34,7 @@
#include "RenderableLightEntityItem.h"
#include "RenderableModelEntityItem.h"
#include "RenderableSphereEntityItem.h"
+#include "RenderableTextEntityItem.h"
QThread* EntityTreeRenderer::getMainThread() {
@@ -45,11 +46,14 @@ QThread* EntityTreeRenderer::getMainThread() {
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts) :
OctreeRenderer(),
_wantScripts(wantScripts),
- _entitiesScriptEngine(NULL) {
+ _entitiesScriptEngine(NULL),
+ _lastMouseEventValid(false)
+{
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
+ REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
@@ -163,10 +167,12 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
QString scriptContents = loadScriptContents(entity->getScript());
- if (QScriptEngine::checkSyntax(scriptContents).state() != QScriptSyntaxCheckResult::Valid) {
+ QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(scriptContents);
+ if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;
- qDebug() << " INVALID SYNTAX";
- qDebug() << " SCRIPT:" << scriptContents;
+ qDebug() << " " << syntaxCheck.errorMessage() << ":"
+ << syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();
+ qDebug() << " SCRIPT:" << entity->getScript();
return QScriptValue(); // invalid script
}
@@ -175,7 +181,7 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
if (!entityScriptConstructor.isFunction()) {
qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;
qDebug() << " NOT CONSTRUCTOR";
- qDebug() << " SCRIPT:" << scriptContents;
+ qDebug() << " SCRIPT:" << entity->getScript();
return QScriptValue(); // invalid script
}
@@ -198,6 +204,19 @@ void EntityTreeRenderer::update() {
// check to see if the avatar has moved and if we need to handle enter/leave entity logic
checkEnterLeaveEntities();
+
+ // Even if we're not moving the mouse, if we started clicking on an entity and we have
+ // not yet released the hold then this is still considered a holdingClickOnEntity event
+ // and we want to simulate this message here as well as in mouse move
+ if (_lastMouseEventValid && !_currentClickingOnEntityID.isInvalidID()) {
+ emit holdingClickOnEntity(_currentClickingOnEntityID, _lastMouseEvent);
+ QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, _lastMouseEvent);
+ QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID);
+ if (currentClickingEntity.property("holdingClickOnEntity").isValid()) {
+ currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
+ }
+ }
+
}
}
@@ -663,6 +682,14 @@ QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& en
return args;
}
+QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent) {
+ QScriptValueList args;
+ args << entityID.toScriptValue(_entitiesScriptEngine);
+ args << mouseEvent.toScriptValue(_entitiesScriptEngine);
+ return args;
+}
+
+
QScriptValueList EntityTreeRenderer::createEntityArgs(const EntityItemID& entityID) {
QScriptValueList args;
args << entityID.toScriptValue(_entitiesScriptEngine);
@@ -689,6 +716,8 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device
entityScript.property("clickDownOnEntity").call(entityScript, entityScriptArgs);
}
}
+ _lastMouseEvent = MouseEvent(*event, deviceID);
+ _lastMouseEventValid = true;
}
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
@@ -720,10 +749,13 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
// makes it the unknown ID, we just released so we can't be clicking on anything
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID();
+ _lastMouseEvent = MouseEvent(*event, deviceID);
+ _lastMouseEventValid = true;
}
void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent");
+
PickRay ray = computePickRay(event->x(), event->y());
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock);
if (rayPickResult.intersects) {
@@ -805,6 +837,8 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
}
}
+ _lastMouseEvent = MouseEvent(*event, deviceID);
+ _lastMouseEventValid = true;
}
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h
index 66107b368a..7a8155cd6b 100644
--- a/interface/src/entities/EntityTreeRenderer.h
+++ b/interface/src/entities/EntityTreeRenderer.h
@@ -56,7 +56,8 @@ public:
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
virtual void init();
- virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::RenderSide renderSide = RenderArgs::MONO);
+ virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
+ RenderArgs::RenderSide renderSide = RenderArgs::MONO);
virtual const FBXGeometry* getGeometryForEntity(const EntityItem* entityItem);
virtual const Model* getModelForEntityItem(const EntityItem* entityItem);
@@ -128,8 +129,12 @@ private:
QScriptValue loadEntityScript(const EntityItemID& entityItemID);
QString loadScriptContents(const QString& scriptMaybeURLorText);
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID);
+ QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent);
QHash _entityScripts;
+
+ bool _lastMouseEventValid;
+ MouseEvent _lastMouseEvent;
};
#endif // hifi_EntityTreeRenderer_h
diff --git a/interface/src/entities/RenderableTextEntityItem.cpp b/interface/src/entities/RenderableTextEntityItem.cpp
new file mode 100644
index 0000000000..f790f6fa35
--- /dev/null
+++ b/interface/src/entities/RenderableTextEntityItem.cpp
@@ -0,0 +1,110 @@
+//
+// RenderableTextEntityItem.cpp
+// interface/src
+//
+// Created by Brad Hefta-Gaub on 8/6/14.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include
+
+#include "InterfaceConfig.h"
+
+#include
+#include
+
+#include "Menu.h"
+#include "EntityTreeRenderer.h"
+#include "RenderableTextEntityItem.h"
+
+const int FIXED_FONT_POINT_SIZE = 40;
+const float LINE_SCALE_RATIO = 1.2f;
+
+EntityItem* RenderableTextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+ return new RenderableTextEntityItem(entityID, properties);
+}
+
+void RenderableTextEntityItem::render(RenderArgs* args) {
+ PerformanceTimer perfTimer("RenderableTextEntityItem::render");
+ assert(getType() == EntityTypes::Text);
+ glm::vec3 position = getPositionInMeters();
+ glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
+ glm::vec3 halfDimensions = dimensions / 2.0f;
+ glm::quat rotation = getRotation();
+ float leftMargin = 0.1f;
+ float rightMargin = 0.1f;
+ float topMargin = 0.1f;
+ float bottomMargin = 0.1f;
+
+ //qDebug() << "RenderableTextEntityItem::render() id:" << getEntityItemID() << "text:" << getText();
+
+ glPushMatrix();
+ {
+ glTranslatef(position.x, position.y, position.z);
+ glm::vec3 axis = glm::axis(rotation);
+ glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
+
+ const float MAX_COLOR = 255.0f;
+ xColor backgroundColor = getBackgroundColorX();
+ float alpha = 1.0f; //getBackgroundAlpha();
+ glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, alpha);
+
+ const float SLIGHTLY_BEHIND = -0.005f;
+
+ glBegin(GL_QUADS);
+ glVertex3f(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
+ glVertex3f(halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
+ glVertex3f(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
+ glVertex3f(-halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
+ glEnd();
+
+ const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation
+
+ // Same font properties as textWidth()
+ TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
+ float maxHeight = (float)textRenderer->calculateHeight("Xy") * LINE_SCALE_RATIO;
+
+ float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
+
+ glTranslatef(-(halfDimensions.x - leftMargin), halfDimensions.y - topMargin, 0.0f);
+
+ glm::vec2 clipMinimum(0.0f, 0.0f);
+ glm::vec2 clipDimensions((dimensions.x - (leftMargin + rightMargin)) / scaleFactor,
+ (dimensions.y - (topMargin + bottomMargin)) / scaleFactor);
+
+ glScalef(scaleFactor, -scaleFactor, 1.0);
+ enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipDimensions.x);
+ enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x);
+ enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipDimensions.y);
+ enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y);
+
+ xColor textColor = getTextColorX();
+ glColor3f(textColor.red / MAX_COLOR, textColor.green / MAX_COLOR, textColor.blue / MAX_COLOR);
+ QStringList lines = _text.split("\n");
+ int lineOffset = maxHeight;
+ float textAlpha = 1.0f; // getTextAlpha()
+ foreach(QString thisLine, lines) {
+ textRenderer->draw(0, lineOffset, qPrintable(thisLine), textAlpha);
+ lineOffset += maxHeight;
+ }
+
+ glDisable(GL_CLIP_PLANE0);
+ glDisable(GL_CLIP_PLANE1);
+ glDisable(GL_CLIP_PLANE2);
+ glDisable(GL_CLIP_PLANE3);
+
+ }
+ glPopMatrix();
+}
+
+void RenderableTextEntityItem::enableClipPlane(GLenum plane, float x, float y, float z, float w) {
+ GLdouble coefficients[] = { x, y, z, w };
+ glClipPlane(plane, coefficients);
+ glEnable(plane);
+}
+
+
+
diff --git a/interface/src/entities/RenderableTextEntityItem.h b/interface/src/entities/RenderableTextEntityItem.h
new file mode 100644
index 0000000000..8760cb9df7
--- /dev/null
+++ b/interface/src/entities/RenderableTextEntityItem.h
@@ -0,0 +1,44 @@
+//
+// RenderableTextEntityItem.h
+// interface/src/entities
+//
+// Created by Brad Hefta-Gaub on 8/6/14.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_RenderableTextEntityItem_h
+#define hifi_RenderableTextEntityItem_h
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+class RenderableTextEntityItem : public TextEntityItem {
+public:
+ static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
+
+ RenderableTextEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
+ TextEntityItem(entityItemID, properties)
+ { }
+
+ virtual void render(RenderArgs* args);
+
+private:
+ void enableClipPlane(GLenum plane, float x, float y, float z, float w);
+
+};
+
+
+#endif // hifi_RenderableTextEntityItem_h
diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp
index 8a79cad6e1..591ae87560 100644
--- a/interface/src/scripting/WindowScriptingInterface.cpp
+++ b/interface/src/scripting/WindowScriptingInterface.cpp
@@ -252,6 +252,7 @@ QScriptValue WindowScriptingInterface::doPeekNonBlockingFormResult(QScriptValue
int e = -1;
int d = -1;
int c = -1;
+ int h = -1;
for (int i = 0; i < _form.property("length").toInt32(); ++i) {
QScriptValue item = _form.property(i);
QScriptValue value = item.property("value");
@@ -275,6 +276,11 @@ QScriptValue WindowScriptingInterface::doPeekNonBlockingFormResult(QScriptValue
array.engine()->undefinedValue()
);
_form.setProperty(i, item);
+ } else if (item.property("type").toString() == "checkbox") {
+ h++;
+ value = _checks.at(h)->checkState() == Qt::Checked;
+ item.setProperty("value", value);
+ _form.setProperty(i, item);
} else {
e += 1;
bool ok = true;
@@ -309,6 +315,7 @@ QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue a
int e = -1;
int d = -1;
int c = -1;
+ int h = -1;
for (int i = 0; i < _form.property("length").toInt32(); ++i) {
QScriptValue item = _form.property(i);
QScriptValue value = item.property("value");
@@ -332,6 +339,11 @@ QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue a
array.engine()->undefinedValue()
);
_form.setProperty(i, item);
+ } else if (item.property("type").toString() == "checkbox") {
+ h++;
+ value = _checks.at(h)->checkState() == Qt::Checked;
+ item.setProperty("value", value);
+ _form.setProperty(i, item);
} else {
e += 1;
bool ok = true;
@@ -362,6 +374,7 @@ QScriptValue WindowScriptingInterface::doGetNonBlockingFormResult(QScriptValue a
_edits.clear();
_directories.clear();
_combos.clear();
+ _checks.clear();
array = _form;
return (_formResult == QDialog::Accepted);
@@ -387,6 +400,7 @@ QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptVal
int e = -1;
int d = -1;
int c = -1;
+ int h = -1;
for (int i = 0; i < form.property("length").toInt32(); ++i) {
QScriptValue item = form.property(i);
QScriptValue value = item.property("value");
@@ -410,6 +424,11 @@ QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptVal
form.engine()->undefinedValue()
);
form.setProperty(i, item);
+ } else if (item.property("type").toString() == "checkbox") {
+ h++;
+ value = _checks.at(h)->checkState() == Qt::Checked;
+ item.setProperty("value", value);
+ form.setProperty(i, item);
} else {
e += 1;
bool ok = true;
@@ -436,6 +455,7 @@ QScriptValue WindowScriptingInterface::showForm(const QString& title, QScriptVal
delete editDialog;
_combos.clear();
+ _checks.clear();
_edits.clear();
_directories.clear();
return (result == QDialog::Accepted);
@@ -522,6 +542,12 @@ QDialog* WindowScriptingInterface::createForm(const QString& title, QScriptValue
}
_combos.push_back(combo);
formLayout->addRow(new QLabel(item.property("label").toString()), combo);
+ } else if (item.property("type").toString() == "checkbox") {
+ QCheckBox* check = new QCheckBox();
+ check->setTristate(false);
+ check->setCheckState(item.property("value").toString() == "true" ? Qt::Checked : Qt::Unchecked);
+ _checks.push_back(check);
+ formLayout->addRow(new QLabel(item.property("label").toString()), check);
} else {
QLineEdit* edit = new QLineEdit(item.property("value").toString());
edit->setMinimumWidth(200);
diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h
index 0b320f23a1..05411530df 100644
--- a/interface/src/scripting/WindowScriptingInterface.h
+++ b/interface/src/scripting/WindowScriptingInterface.h
@@ -90,6 +90,7 @@ private:
bool _nonBlockingFormActive;
int _formResult;
QVector _combos;
+ QVector _checks;
QVector _edits;
QVector _directories;
};
diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp
index a83772d01b..5a5bfcd937 100644
--- a/interface/src/ui/overlays/Base3DOverlay.cpp
+++ b/interface/src/ui/overlays/Base3DOverlay.cpp
@@ -31,6 +31,17 @@ Base3DOverlay::Base3DOverlay() :
{
}
+Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
+ Overlay(base3DOverlay),
+ _position(base3DOverlay->_position),
+ _lineWidth(base3DOverlay->_lineWidth),
+ _rotation(base3DOverlay->_rotation),
+ _isSolid(base3DOverlay->_isSolid),
+ _isDashedLine(base3DOverlay->_isDashedLine),
+ _ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection)
+{
+}
+
Base3DOverlay::~Base3DOverlay() {
}
@@ -178,8 +189,4 @@ void Base3DOverlay::drawDashedLine(const glm::vec3& start, const glm::vec3& end)
glVertex3f(end.x, end.y, end.z);
glEnd();
-
}
-
-
-
diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h
index 7b7fa1a8f4..8304883e3c 100644
--- a/interface/src/ui/overlays/Base3DOverlay.h
+++ b/interface/src/ui/overlays/Base3DOverlay.h
@@ -23,6 +23,7 @@ class Base3DOverlay : public Overlay {
public:
Base3DOverlay();
+ Base3DOverlay(const Base3DOverlay* base3DOverlay);
~Base3DOverlay();
// getters
@@ -64,6 +65,5 @@ protected:
bool _isDashedLine;
bool _ignoreRayIntersection;
};
-
#endif // hifi_Base3DOverlay_h
diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp
index b36202cb04..5fbad7839a 100644
--- a/interface/src/ui/overlays/BillboardOverlay.cpp
+++ b/interface/src/ui/overlays/BillboardOverlay.cpp
@@ -24,6 +24,19 @@ BillboardOverlay::BillboardOverlay() :
_isLoaded = false;
}
+BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) :
+ Base3DOverlay(billboardOverlay),
+ _url(billboardOverlay->_url),
+ _billboard(billboardOverlay->_billboard),
+ _size(),
+ _billboardTexture(),
+ _newTextureNeeded(true),
+ _fromImage(billboardOverlay->_fromImage),
+ _scale(billboardOverlay->_scale),
+ _isFacingAvatar(billboardOverlay->_isFacingAvatar)
+{
+}
+
void BillboardOverlay::render(RenderArgs* args) {
if (!_visible || !_isLoaded) {
return;
@@ -215,3 +228,6 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v
return false;
}
+BillboardOverlay* BillboardOverlay::createClone() const {
+ return new BillboardOverlay(this);
+}
diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h
index 3a22a247f0..be947acf98 100644
--- a/interface/src/ui/overlays/BillboardOverlay.h
+++ b/interface/src/ui/overlays/BillboardOverlay.h
@@ -22,7 +22,8 @@ class BillboardOverlay : public Base3DOverlay {
Q_OBJECT
public:
BillboardOverlay();
-
+ BillboardOverlay(const BillboardOverlay* billboardOverlay);
+
virtual void render(RenderArgs* args);
// setters
@@ -36,6 +37,8 @@ public:
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
+ virtual BillboardOverlay* createClone() const;
+
private slots:
void replyFinished();
diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp
index 986088cebb..d19297b589 100644
--- a/interface/src/ui/overlays/Circle3DOverlay.cpp
+++ b/interface/src/ui/overlays/Circle3DOverlay.cpp
@@ -33,6 +33,22 @@ Circle3DOverlay::Circle3DOverlay() :
_minorTickMarksColor.red = _minorTickMarksColor.green = _minorTickMarksColor.blue = (unsigned char)0;
}
+Circle3DOverlay::Circle3DOverlay(const Circle3DOverlay* circle3DOverlay) :
+ Planar3DOverlay(circle3DOverlay),
+ _startAt(circle3DOverlay->_startAt),
+ _endAt(circle3DOverlay->_endAt),
+ _outerRadius(circle3DOverlay->_outerRadius),
+ _innerRadius(circle3DOverlay->_innerRadius),
+ _hasTickMarks(circle3DOverlay->_hasTickMarks),
+ _majorTickMarksAngle(circle3DOverlay->_majorTickMarksAngle),
+ _minorTickMarksAngle(circle3DOverlay->_minorTickMarksAngle),
+ _majorTickMarksLength(circle3DOverlay->_majorTickMarksLength),
+ _minorTickMarksLength(circle3DOverlay->_minorTickMarksLength),
+ _majorTickMarksColor(circle3DOverlay->_majorTickMarksColor),
+ _minorTickMarksColor(circle3DOverlay->_minorTickMarksColor)
+{
+}
+
Circle3DOverlay::~Circle3DOverlay() {
}
@@ -358,9 +374,6 @@ bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin,
return intersects;
}
-
-
-
-
-
-
+Circle3DOverlay* Circle3DOverlay::createClone() const {
+ return new Circle3DOverlay(this);
+}
diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h
index 816ed280f9..b428be7a43 100644
--- a/interface/src/ui/overlays/Circle3DOverlay.h
+++ b/interface/src/ui/overlays/Circle3DOverlay.h
@@ -18,6 +18,7 @@ class Circle3DOverlay : public Planar3DOverlay {
public:
Circle3DOverlay();
+ Circle3DOverlay(const Circle3DOverlay* circle3DOverlay);
~Circle3DOverlay();
virtual void render(RenderArgs* args);
virtual void setProperties(const QScriptValue& properties);
@@ -48,6 +49,8 @@ public:
void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const;
+
+ virtual Circle3DOverlay* createClone() const;
protected:
float _startAt;
diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp
index 81a2dfd498..9c478c4465 100644
--- a/interface/src/ui/overlays/Cube3DOverlay.cpp
+++ b/interface/src/ui/overlays/Cube3DOverlay.cpp
@@ -22,6 +22,11 @@
Cube3DOverlay::Cube3DOverlay() {
}
+Cube3DOverlay::Cube3DOverlay(const Cube3DOverlay* cube3DOverlay) :
+ Volume3DOverlay(cube3DOverlay)
+{
+}
+
Cube3DOverlay::~Cube3DOverlay() {
}
@@ -102,3 +107,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
delete glower;
}
}
+
+Cube3DOverlay* Cube3DOverlay::createClone() const {
+ return new Cube3DOverlay(this);
+}
diff --git a/interface/src/ui/overlays/Cube3DOverlay.h b/interface/src/ui/overlays/Cube3DOverlay.h
index 272d68bb5e..9c199f2b7b 100644
--- a/interface/src/ui/overlays/Cube3DOverlay.h
+++ b/interface/src/ui/overlays/Cube3DOverlay.h
@@ -18,8 +18,11 @@ class Cube3DOverlay : public Volume3DOverlay {
public:
Cube3DOverlay();
+ Cube3DOverlay(const Cube3DOverlay* cube3DOverlay);
~Cube3DOverlay();
virtual void render(RenderArgs* args);
+
+ virtual Cube3DOverlay* createClone() const;
};
diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp
index dee5d5d60a..84610d7981 100644
--- a/interface/src/ui/overlays/Grid3DOverlay.cpp
+++ b/interface/src/ui/overlays/Grid3DOverlay.cpp
@@ -20,6 +20,13 @@ Grid3DOverlay::Grid3DOverlay() : Base3DOverlay(),
_majorGridEvery(5) {
}
+Grid3DOverlay::Grid3DOverlay(const Grid3DOverlay* grid3DOverlay) :
+ Base3DOverlay(grid3DOverlay),
+ _minorGridWidth(grid3DOverlay->_minorGridWidth),
+ _majorGridEvery(grid3DOverlay->_majorGridEvery)
+{
+}
+
Grid3DOverlay::~Grid3DOverlay() {
}
@@ -127,3 +134,7 @@ QScriptValue Grid3DOverlay::getProperty(const QString& property) {
return Base3DOverlay::getProperty(property);
}
+
+Grid3DOverlay* Grid3DOverlay::createClone() const {
+ return new Grid3DOverlay(this);
+}
diff --git a/interface/src/ui/overlays/Grid3DOverlay.h b/interface/src/ui/overlays/Grid3DOverlay.h
index 3e6a235d54..b162ff3d74 100644
--- a/interface/src/ui/overlays/Grid3DOverlay.h
+++ b/interface/src/ui/overlays/Grid3DOverlay.h
@@ -29,12 +29,15 @@ class Grid3DOverlay : public Base3DOverlay {
public:
Grid3DOverlay();
+ Grid3DOverlay(const Grid3DOverlay* grid3DOverlay);
~Grid3DOverlay();
virtual void render(RenderArgs* args);
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
+ virtual Grid3DOverlay* createClone() const;
+
private:
float _minorGridWidth;
int _majorGridEvery;
diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp
index 615872e6ef..f903dfe19c 100644
--- a/interface/src/ui/overlays/ImageOverlay.cpp
+++ b/interface/src/ui/overlays/ImageOverlay.cpp
@@ -27,6 +27,18 @@ ImageOverlay::ImageOverlay() :
_isLoaded = false;
}
+ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) :
+ Overlay2D(imageOverlay),
+ _imageURL(imageOverlay->_imageURL),
+ _textureImage(imageOverlay->_textureImage),
+ _textureID(0),
+ _fromImage(),
+ _renderImage(imageOverlay->_renderImage),
+ _textureBound(false),
+ _wantClipFromImage(false)
+{
+}
+
ImageOverlay::~ImageOverlay() {
if (_parent && _textureID) {
// do we need to call this?
@@ -161,3 +173,6 @@ QScriptValue ImageOverlay::getProperty(const QString& property) {
return Overlay2D::getProperty(property);
}
+ImageOverlay* ImageOverlay::createClone() const {
+ return new ImageOverlay(this);
+}
diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h
index bf4f2860ad..cff557654f 100644
--- a/interface/src/ui/overlays/ImageOverlay.h
+++ b/interface/src/ui/overlays/ImageOverlay.h
@@ -33,6 +33,7 @@ class ImageOverlay : public Overlay2D {
public:
ImageOverlay();
+ ImageOverlay(const ImageOverlay* imageOverlay);
~ImageOverlay();
virtual void render(RenderArgs* args);
@@ -46,6 +47,8 @@ public:
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
+ virtual ImageOverlay* createClone() const;
+
private slots:
void replyFinished(); // we actually want to hide this...
diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp
index 896ebd1e68..ae67bf9d92 100644
--- a/interface/src/ui/overlays/Line3DOverlay.cpp
+++ b/interface/src/ui/overlays/Line3DOverlay.cpp
@@ -18,6 +18,12 @@
Line3DOverlay::Line3DOverlay() {
}
+Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) :
+ Base3DOverlay(line3DOverlay),
+ _end(line3DOverlay->_end)
+{
+}
+
Line3DOverlay::~Line3DOverlay() {
}
@@ -87,3 +93,7 @@ QScriptValue Line3DOverlay::getProperty(const QString& property) {
return Base3DOverlay::getProperty(property);
}
+
+Line3DOverlay* Line3DOverlay::createClone() const {
+ return new Line3DOverlay(this);
+}
diff --git a/interface/src/ui/overlays/Line3DOverlay.h b/interface/src/ui/overlays/Line3DOverlay.h
index 0148648c35..607fb0e0bb 100644
--- a/interface/src/ui/overlays/Line3DOverlay.h
+++ b/interface/src/ui/overlays/Line3DOverlay.h
@@ -18,6 +18,7 @@ class Line3DOverlay : public Base3DOverlay {
public:
Line3DOverlay();
+ Line3DOverlay(const Line3DOverlay* line3DOverlay);
~Line3DOverlay();
virtual void render(RenderArgs* args);
@@ -30,6 +31,8 @@ public:
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
+ virtual Line3DOverlay* createClone() const;
+
protected:
glm::vec3 _end;
};
diff --git a/interface/src/ui/overlays/LocalModelsOverlay.cpp b/interface/src/ui/overlays/LocalModelsOverlay.cpp
index c1f16bf13e..d8555c85ba 100644
--- a/interface/src/ui/overlays/LocalModelsOverlay.cpp
+++ b/interface/src/ui/overlays/LocalModelsOverlay.cpp
@@ -18,6 +18,13 @@ LocalModelsOverlay::LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer) :
_entityTreeRenderer(entityTreeRenderer) {
}
+LocalModelsOverlay::LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay) :
+ Volume3DOverlay(localModelsOverlay),
+ _entityTreeRenderer(localModelsOverlay->_entityTreeRenderer)
+{
+
+}
+
LocalModelsOverlay::~LocalModelsOverlay() {
}
@@ -48,3 +55,7 @@ void LocalModelsOverlay::render(RenderArgs* args) {
}
}
+
+LocalModelsOverlay* LocalModelsOverlay::createClone() const {
+ return new LocalModelsOverlay(this);
+}
diff --git a/interface/src/ui/overlays/LocalModelsOverlay.h b/interface/src/ui/overlays/LocalModelsOverlay.h
index 60f09b8666..39465de7f9 100644
--- a/interface/src/ui/overlays/LocalModelsOverlay.h
+++ b/interface/src/ui/overlays/LocalModelsOverlay.h
@@ -20,11 +20,14 @@ class LocalModelsOverlay : public Volume3DOverlay {
Q_OBJECT
public:
LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer);
+ LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay);
~LocalModelsOverlay();
virtual void update(float deltatime);
virtual void render(RenderArgs* args);
+ virtual LocalModelsOverlay* createClone() const;
+
private:
EntityTreeRenderer* _entityTreeRenderer;
};
diff --git a/interface/src/ui/overlays/LocalVoxelsOverlay.cpp b/interface/src/ui/overlays/LocalVoxelsOverlay.cpp
index 3ca79d548c..9cb3df8bf3 100644
--- a/interface/src/ui/overlays/LocalVoxelsOverlay.cpp
+++ b/interface/src/ui/overlays/LocalVoxelsOverlay.cpp
@@ -28,6 +28,12 @@ LocalVoxelsOverlay::LocalVoxelsOverlay() :
{
}
+LocalVoxelsOverlay::LocalVoxelsOverlay(const LocalVoxelsOverlay* localVoxelsOverlay) :
+ Volume3DOverlay(localVoxelsOverlay),
+ _voxelCount(localVoxelsOverlay->_voxelCount)
+{
+}
+
LocalVoxelsOverlay::~LocalVoxelsOverlay() {
_voxelSystem->changeTree(new VoxelTree());
_voxelSystem.clear();
@@ -103,6 +109,10 @@ void LocalVoxelsOverlay::setProperties(const QScriptValue &properties) {
}
}
+LocalVoxelsOverlay* LocalVoxelsOverlay::createClone() const {
+ return new LocalVoxelsOverlay(this);
+}
+
QScriptValue LocalVoxelsOverlay::getProperty(const QString& property) {
if (property == "scale") {
return vec3toScriptValue(_scriptEngine, getDimensions());
diff --git a/interface/src/ui/overlays/LocalVoxelsOverlay.h b/interface/src/ui/overlays/LocalVoxelsOverlay.h
index 25ad4738b9..4afa44f9be 100644
--- a/interface/src/ui/overlays/LocalVoxelsOverlay.h
+++ b/interface/src/ui/overlays/LocalVoxelsOverlay.h
@@ -32,6 +32,7 @@ class LocalVoxelsOverlay : public Volume3DOverlay {
Q_OBJECT
public:
LocalVoxelsOverlay();
+ LocalVoxelsOverlay(const LocalVoxelsOverlay* localVoxelsOverlay);
~LocalVoxelsOverlay();
virtual void update(float deltatime);
@@ -40,6 +41,7 @@ public:
virtual void setProperties(const QScriptValue& properties);
virtual QScriptValue getProperty(const QString& property);
+ virtual LocalVoxelsOverlay* createClone() const;
private:
static QMap _voxelSystemMap; // treeName/voxelSystem
diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp
index a2abfe77b1..60049e0b3b 100644
--- a/interface/src/ui/overlays/ModelOverlay.cpp
+++ b/interface/src/ui/overlays/ModelOverlay.cpp
@@ -22,6 +22,22 @@ ModelOverlay::ModelOverlay()
_isLoaded = false;
}
+ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
+ Base3DOverlay(modelOverlay),
+ _model(),
+ _modelTextures(QVariantMap()),
+ _url(modelOverlay->_url),
+ _rotation(modelOverlay->_rotation),
+ _scale(modelOverlay->_scale),
+ _updateModel(false)
+{
+ _model.init();
+ if (_url.isValid()) {
+ _updateModel = true;
+ _isLoaded = false;
+ }
+}
+
void ModelOverlay::update(float deltatime) {
if (_updateModel) {
_updateModel = false;
@@ -166,4 +182,6 @@ bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const g
return _model.findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo);
}
-
+ModelOverlay* ModelOverlay::createClone() const {
+ return new ModelOverlay(this);
+}
diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h
index c9f6799e8c..80b52ea27e 100644
--- a/interface/src/ui/overlays/ModelOverlay.h
+++ b/interface/src/ui/overlays/ModelOverlay.h
@@ -20,7 +20,8 @@ class ModelOverlay : public Base3DOverlay {
Q_OBJECT
public:
ModelOverlay();
-
+ ModelOverlay(const ModelOverlay* modelOverlay);
+
virtual void update(float deltatime);
virtual void render(RenderArgs* args);
virtual void setProperties(const QScriptValue& properties);
@@ -29,8 +30,10 @@ public:
virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face, QString& extraInfo) const;
+ virtual ModelOverlay* createClone() const;
+
private:
-
+
Model _model;
QVariantMap _modelTextures;
diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp
index a7c38946b7..fd7aaca717 100644
--- a/interface/src/ui/overlays/Overlay.cpp
+++ b/interface/src/ui/overlays/Overlay.cpp
@@ -39,12 +39,32 @@ Overlay::Overlay() :
{
}
+Overlay::Overlay(const Overlay* overlay) :
+ _parent(NULL),
+ _isLoaded(overlay->_isLoaded),
+ _alpha(overlay->_alpha),
+ _glowLevel(overlay->_glowLevel),
+ _pulse(overlay->_pulse),
+ _pulseMax(overlay->_pulseMax),
+ _pulseMin(overlay->_pulseMin),
+ _pulsePeriod(overlay->_pulsePeriod),
+ _pulseDirection(overlay->_pulseDirection),
+ _lastPulseUpdate(usecTimestampNow()),
+ _glowLevelPulse(overlay->_glowLevelPulse),
+ _alphaPulse(overlay->_alphaPulse),
+ _colorPulse(overlay->_colorPulse),
+ _color(overlay->_color),
+ _visible(overlay->_visible),
+ _anchor(overlay->_anchor),
+ _scriptEngine(NULL)
+{
+}
+
void Overlay::init(QGLWidget* parent, QScriptEngine* scriptEngine) {
_parent = parent;
_scriptEngine = scriptEngine;
}
-
Overlay::~Overlay() {
}
diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h
index 192bb5fd40..99ab588197 100644
--- a/interface/src/ui/overlays/Overlay.h
+++ b/interface/src/ui/overlays/Overlay.h
@@ -36,6 +36,7 @@ public:
};
Overlay();
+ Overlay(const Overlay* overlay);
~Overlay();
void init(QGLWidget* parent, QScriptEngine* scriptEngine);
virtual void update(float deltatime) {}
@@ -78,6 +79,7 @@ public:
void setAlphaPulse(float value) { _alphaPulse = value; }
virtual void setProperties(const QScriptValue& properties);
+ virtual Overlay* createClone() const = 0;
virtual QScriptValue getProperty(const QString& property);
protected:
diff --git a/interface/src/ui/overlays/Overlay2D.cpp b/interface/src/ui/overlays/Overlay2D.cpp
index b7c0a3a3e4..df7df391eb 100644
--- a/interface/src/ui/overlays/Overlay2D.cpp
+++ b/interface/src/ui/overlays/Overlay2D.cpp
@@ -22,6 +22,12 @@
Overlay2D::Overlay2D() {
}
+Overlay2D::Overlay2D(const Overlay2D* overlay2D) :
+ Overlay(overlay2D),
+ _bounds(overlay2D->_bounds)
+{
+}
+
Overlay2D::~Overlay2D() {
}
diff --git a/interface/src/ui/overlays/Overlay2D.h b/interface/src/ui/overlays/Overlay2D.h
index d0d75c9397..7493add905 100644
--- a/interface/src/ui/overlays/Overlay2D.h
+++ b/interface/src/ui/overlays/Overlay2D.h
@@ -28,6 +28,7 @@ class Overlay2D : public Overlay {
public:
Overlay2D();
+ Overlay2D(const Overlay2D* overlay2D);
~Overlay2D();
virtual bool is3D() const { return false; }
@@ -43,7 +44,7 @@ public:
void setX(int x) { _bounds.setX(x); }
void setY(int y) { _bounds.setY(y); }
void setWidth(int width) { _bounds.setWidth(width); }
- void setHeight(int height) { _bounds.setHeight(height); }
+ void setHeight(int height) { _bounds.setHeight(height); }
void setBounds(const QRect& bounds) { _bounds = bounds; }
virtual void setProperties(const QScriptValue& properties);
diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp
index 71980ba92c..d6dbb0861e 100644
--- a/interface/src/ui/overlays/Overlays.cpp
+++ b/interface/src/ui/overlays/Overlays.cpp
@@ -196,6 +196,16 @@ unsigned int Overlays::addOverlay(Overlay* overlay) {
return thisID;
}
+unsigned int Overlays::cloneOverlay(unsigned int id) {
+ Overlay* thisOverlay = NULL;
+ if (_overlays2D.contains(id)) {
+ thisOverlay = _overlays2D[id];
+ } else if (_overlays3D.contains(id)) {
+ thisOverlay = _overlays3D[id];
+ }
+ return addOverlay(thisOverlay->createClone());
+}
+
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
Overlay* thisOverlay = NULL;
QWriteLocker lock(&_lock);
@@ -397,15 +407,15 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
bool Overlays::isLoaded(unsigned int id) {
QReadLocker lock(&_lock);
- Overlay* overlay = _overlays2D.value(id);
- if (!overlay) {
- _overlays3D.value(id);
- }
- if (!overlay) {
+ Overlay* thisOverlay = NULL;
+ if (_overlays2D.contains(id)) {
+ thisOverlay = _overlays2D[id];
+ } else if (_overlays3D.contains(id)) {
+ thisOverlay = _overlays3D[id];
+ } else {
return false; // not found
}
-
- return overlay->isLoaded();
+ return thisOverlay->isLoaded();
}
float Overlays::textWidth(unsigned int id, const QString& text) const {
diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h
index d49ffcc3d0..2cd80041cd 100644
--- a/interface/src/ui/overlays/Overlays.h
+++ b/interface/src/ui/overlays/Overlays.h
@@ -51,7 +51,8 @@ public:
~Overlays();
void init(QGLWidget* parent);
void update(float deltatime);
- void render3D(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::RenderSide renderSide = RenderArgs::MONO);
+ void render3D(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
+ RenderArgs::RenderSide renderSide = RenderArgs::MONO);
void render2D();
public slots:
@@ -61,6 +62,9 @@ public slots:
/// adds an overlay that's already been created
unsigned int addOverlay(Overlay* overlay);
+ /// clones an existing overlay
+ unsigned int cloneOverlay(unsigned int id);
+
/// edits an overlay updating only the included properties, will return the identified OverlayID in case of
/// successful edit, if the input id is for an unknown overlay this function will have no effect
bool editOverlay(unsigned int id, const QScriptValue& properties);
diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp
index ffd7d73531..a8288b241c 100644
--- a/interface/src/ui/overlays/Planar3DOverlay.cpp
+++ b/interface/src/ui/overlays/Planar3DOverlay.cpp
@@ -26,6 +26,12 @@ Planar3DOverlay::Planar3DOverlay() :
{
}
+Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) :
+ Base3DOverlay(planar3DOverlay),
+ _dimensions(planar3DOverlay->_dimensions)
+{
+}
+
Planar3DOverlay::~Planar3DOverlay() {
}
diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h
index fe73cfbe08..d34fe44ebc 100644
--- a/interface/src/ui/overlays/Planar3DOverlay.h
+++ b/interface/src/ui/overlays/Planar3DOverlay.h
@@ -26,6 +26,7 @@ class Planar3DOverlay : public Base3DOverlay {
public:
Planar3DOverlay();
+ Planar3DOverlay(const Planar3DOverlay* planar3DOverlay);
~Planar3DOverlay();
// getters
diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp
index c131a139ac..bcca759cd4 100644
--- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp
+++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp
@@ -20,6 +20,11 @@
Rectangle3DOverlay::Rectangle3DOverlay() {
}
+Rectangle3DOverlay::Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay) :
+ Planar3DOverlay(rectangle3DOverlay)
+{
+}
+
Rectangle3DOverlay::~Rectangle3DOverlay() {
}
@@ -106,9 +111,6 @@ void Rectangle3DOverlay::setProperties(const QScriptValue &properties) {
Planar3DOverlay::setProperties(properties);
}
-
-
-
-
-
-
+Rectangle3DOverlay* Rectangle3DOverlay::createClone() const {
+ return new Rectangle3DOverlay(this);
+}
diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.h b/interface/src/ui/overlays/Rectangle3DOverlay.h
index 9cec6da7e7..5e75469f2d 100644
--- a/interface/src/ui/overlays/Rectangle3DOverlay.h
+++ b/interface/src/ui/overlays/Rectangle3DOverlay.h
@@ -18,9 +18,12 @@ class Rectangle3DOverlay : public Planar3DOverlay {
public:
Rectangle3DOverlay();
+ Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay);
~Rectangle3DOverlay();
virtual void render(RenderArgs* args);
virtual void setProperties(const QScriptValue& properties);
+
+ virtual Rectangle3DOverlay* createClone() const;
};
diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp
index 63ed3e8d7e..34064675e5 100644
--- a/interface/src/ui/overlays/Sphere3DOverlay.cpp
+++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp
@@ -21,6 +21,11 @@
Sphere3DOverlay::Sphere3DOverlay() {
}
+Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
+ Volume3DOverlay(Sphere3DOverlay)
+{
+}
+
Sphere3DOverlay::~Sphere3DOverlay() {
}
@@ -70,3 +75,7 @@ void Sphere3DOverlay::render(RenderArgs* args) {
}
}
+
+Sphere3DOverlay* Sphere3DOverlay::createClone() const {
+ return new Sphere3DOverlay(this);
+}
diff --git a/interface/src/ui/overlays/Sphere3DOverlay.h b/interface/src/ui/overlays/Sphere3DOverlay.h
index 98776a3f57..3b881e5ba6 100644
--- a/interface/src/ui/overlays/Sphere3DOverlay.h
+++ b/interface/src/ui/overlays/Sphere3DOverlay.h
@@ -18,8 +18,11 @@ class Sphere3DOverlay : public Volume3DOverlay {
public:
Sphere3DOverlay();
+ Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay);
~Sphere3DOverlay();
virtual void render(RenderArgs* args);
+
+ virtual Sphere3DOverlay* createClone() const;
};
diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp
index 8a8581599a..88ac009ac7 100644
--- a/interface/src/ui/overlays/Text3DOverlay.cpp
+++ b/interface/src/ui/overlays/Text3DOverlay.cpp
@@ -31,6 +31,19 @@ Text3DOverlay::Text3DOverlay() :
{
}
+Text3DOverlay::Text3DOverlay(const Text3DOverlay* text3DOverlay) :
+ Planar3DOverlay(text3DOverlay),
+ _text(text3DOverlay->_text),
+ _backgroundColor(text3DOverlay->_backgroundColor),
+ _lineHeight(text3DOverlay->_lineHeight),
+ _leftMargin(text3DOverlay->_leftMargin),
+ _topMargin(text3DOverlay->_topMargin),
+ _rightMargin(text3DOverlay->_rightMargin),
+ _bottomMargin(text3DOverlay->_bottomMargin),
+ _isFacingAvatar(text3DOverlay->_isFacingAvatar)
+{
+}
+
Text3DOverlay::~Text3DOverlay() {
}
@@ -172,7 +185,6 @@ void Text3DOverlay::setProperties(const QScriptValue& properties) {
setBottomMargin(properties.property("bottomMargin").toVariant().toFloat());
}
-
QScriptValue isFacingAvatarValue = properties.property("isFacingAvatar");
if (isFacingAvatarValue.isValid()) {
_isFacingAvatar = isFacingAvatarValue.toVariant().toBool();
@@ -208,6 +220,10 @@ QScriptValue Text3DOverlay::getProperty(const QString& property) {
return Planar3DOverlay::getProperty(property);
}
+Text3DOverlay* Text3DOverlay::createClone() const {
+ return new Text3DOverlay(this);;
+}
+
float Text3DOverlay::textWidth(const QString& text) const {
QFont font(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); // Same font properties as render()
QFontMetrics fontMetrics(font);
@@ -215,3 +231,4 @@ float Text3DOverlay::textWidth(const QString& text) const {
return scaleFactor * (float)fontMetrics.width(qPrintable(text));
}
+
diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h
index ceb52b492e..c624830ac8 100644
--- a/interface/src/ui/overlays/Text3DOverlay.h
+++ b/interface/src/ui/overlays/Text3DOverlay.h
@@ -24,6 +24,7 @@ class Text3DOverlay : public Planar3DOverlay {
public:
Text3DOverlay();
+ Text3DOverlay(const Text3DOverlay* text3DOverlay);
~Text3DOverlay();
virtual void render(RenderArgs* args);
@@ -51,6 +52,8 @@ public:
float textWidth(const QString& text) const; // Meters
+ virtual Text3DOverlay* createClone() const;
+
private:
void enableClipPlane(GLenum plane, float x, float y, float z, float w);
diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp
index 1c3a626132..1d72cf7f05 100644
--- a/interface/src/ui/overlays/TextOverlay.cpp
+++ b/interface/src/ui/overlays/TextOverlay.cpp
@@ -25,6 +25,16 @@ TextOverlay::TextOverlay() :
{
}
+TextOverlay::TextOverlay(const TextOverlay* textOverlay) :
+ Overlay2D(textOverlay),
+ _text(textOverlay->_text),
+ _backgroundColor(textOverlay->_backgroundColor),
+ _leftMargin(textOverlay->_leftMargin),
+ _topMargin(textOverlay->_topMargin),
+ _fontSize(textOverlay->_fontSize)
+{
+}
+
TextOverlay::~TextOverlay() {
}
@@ -124,6 +134,10 @@ void TextOverlay::setProperties(const QScriptValue& properties) {
}
}
+TextOverlay* TextOverlay::createClone() const {
+ return new TextOverlay(this);
+}
+
QScriptValue TextOverlay::getProperty(const QString& property) {
if (property == "font") {
QScriptValue font = _scriptEngine->newObject();
@@ -146,7 +160,6 @@ QScriptValue TextOverlay::getProperty(const QString& property) {
return Overlay2D::getProperty(property);
}
-
float TextOverlay::textWidth(const QString& text) const {
QFont font(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); // Same font properties as render()
QFontMetrics fontMetrics(font);
diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h
index 7559b33df5..703e15be10 100644
--- a/interface/src/ui/overlays/TextOverlay.h
+++ b/interface/src/ui/overlays/TextOverlay.h
@@ -37,6 +37,7 @@ class TextOverlay : public Overlay2D {
public:
TextOverlay();
+ TextOverlay(const TextOverlay* textOverlay);
~TextOverlay();
virtual void render(RenderArgs* args);
@@ -53,12 +54,12 @@ public:
void setFontSize(int fontSize) { _fontSize = fontSize; }
virtual void setProperties(const QScriptValue& properties);
+ virtual TextOverlay* createClone() const;
virtual QScriptValue getProperty(const QString& property);
float textWidth(const QString& text) const; // Pixels
private:
-
QString _text;
xColor _backgroundColor;
int _leftMargin;
diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp
index 0940caea04..c4192a15b2 100644
--- a/interface/src/ui/overlays/Volume3DOverlay.cpp
+++ b/interface/src/ui/overlays/Volume3DOverlay.cpp
@@ -28,6 +28,12 @@ Volume3DOverlay::Volume3DOverlay() :
{
}
+Volume3DOverlay::Volume3DOverlay(const Volume3DOverlay* volume3DOverlay) :
+ Base3DOverlay(volume3DOverlay),
+ _dimensions(volume3DOverlay->_dimensions)
+{
+}
+
Volume3DOverlay::~Volume3DOverlay() {
}
diff --git a/interface/src/ui/overlays/Volume3DOverlay.h b/interface/src/ui/overlays/Volume3DOverlay.h
index 8787759022..005646c036 100644
--- a/interface/src/ui/overlays/Volume3DOverlay.h
+++ b/interface/src/ui/overlays/Volume3DOverlay.h
@@ -26,6 +26,7 @@ class Volume3DOverlay : public Base3DOverlay {
public:
Volume3DOverlay();
+ Volume3DOverlay(const Volume3DOverlay* volume3DOverlay);
~Volume3DOverlay();
// getters
diff --git a/libraries/audio/src/AudioScriptingInterface.h b/libraries/audio/src/AudioScriptingInterface.h
index 0017806b40..5b67666a97 100644
--- a/libraries/audio/src/AudioScriptingInterface.h
+++ b/libraries/audio/src/AudioScriptingInterface.h
@@ -37,6 +37,10 @@ public slots:
void injectorStopped();
+signals:
+ void mutedByMixer();
+ void environmentMuted();
+
private:
AudioScriptingInterface();
QList< QPointer > _activeInjectors;
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 75fa05032a..11c9646cbc 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -20,93 +20,60 @@
#include "EntityItem.h"
#include "EntityItemProperties.h"
#include "ModelEntityItem.h"
+#include "TextEntityItem.h"
EntityItemProperties::EntityItemProperties() :
+ CONSTRUCT_PROPERTY(visible, EntityItem::DEFAULT_VISIBLE),
+ CONSTRUCT_PROPERTY(position, 0),
+ CONSTRUCT_PROPERTY(dimensions, EntityItem::DEFAULT_DIMENSIONS),
+ CONSTRUCT_PROPERTY(rotation, EntityItem::DEFAULT_ROTATION),
+ CONSTRUCT_PROPERTY(mass, EntityItem::DEFAULT_MASS),
+ CONSTRUCT_PROPERTY(velocity, EntityItem::DEFAULT_VELOCITY),
+ CONSTRUCT_PROPERTY(gravity, EntityItem::DEFAULT_GRAVITY),
+ CONSTRUCT_PROPERTY(damping, EntityItem::DEFAULT_DAMPING),
+ CONSTRUCT_PROPERTY(lifetime, EntityItem::DEFAULT_LIFETIME),
+ CONSTRUCT_PROPERTY(script, EntityItem::DEFAULT_SCRIPT),
+ CONSTRUCT_PROPERTY(color, ),
+ CONSTRUCT_PROPERTY(modelURL, ""),
+ CONSTRUCT_PROPERTY(animationURL, ""),
+ CONSTRUCT_PROPERTY(animationFPS, ModelEntityItem::DEFAULT_ANIMATION_FPS),
+ CONSTRUCT_PROPERTY(animationFrameIndex, ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
+ CONSTRUCT_PROPERTY(animationIsPlaying, ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING),
+ CONSTRUCT_PROPERTY(registrationPoint, EntityItem::DEFAULT_REGISTRATION_POINT),
+ CONSTRUCT_PROPERTY(angularVelocity, EntityItem::DEFAULT_ANGULAR_VELOCITY),
+ CONSTRUCT_PROPERTY(angularDamping, EntityItem::DEFAULT_ANGULAR_DAMPING),
+ CONSTRUCT_PROPERTY(ignoreForCollisions, EntityItem::DEFAULT_IGNORE_FOR_COLLISIONS),
+ CONSTRUCT_PROPERTY(collisionsWillMove, EntityItem::DEFAULT_COLLISIONS_WILL_MOVE),
+ CONSTRUCT_PROPERTY(isSpotlight, false),
+ CONSTRUCT_PROPERTY(diffuseColor, ),
+ CONSTRUCT_PROPERTY(ambientColor, ),
+ CONSTRUCT_PROPERTY(specularColor, ),
+ CONSTRUCT_PROPERTY(constantAttenuation, 1.0f),
+ CONSTRUCT_PROPERTY(linearAttenuation, 0.0f),
+ CONSTRUCT_PROPERTY(quadraticAttenuation, 0.0f),
+ CONSTRUCT_PROPERTY(exponent, 0.0f),
+ CONSTRUCT_PROPERTY(cutoff, PI),
+ CONSTRUCT_PROPERTY(locked, false),
+ CONSTRUCT_PROPERTY(textures, ""),
+ CONSTRUCT_PROPERTY(animationSettings, ""),
+ CONSTRUCT_PROPERTY(userData, EntityItem::DEFAULT_USER_DATA),
+ CONSTRUCT_PROPERTY(text, TextEntityItem::DEFAULT_TEXT),
+ CONSTRUCT_PROPERTY(lineHeight, TextEntityItem::DEFAULT_LINE_HEIGHT),
+ CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR),
+ CONSTRUCT_PROPERTY(backgroundColor, TextEntityItem::DEFAULT_BACKGROUND_COLOR),
+
_id(UNKNOWN_ENTITY_ID),
_idSet(false),
- _lastEdited(0), // ????
+ _lastEdited(0),
_created(UNKNOWN_CREATED_TIME),
_type(EntityTypes::Unknown),
- _position(0),
- _dimensions(EntityItem::DEFAULT_DIMENSIONS),
- _rotation(EntityItem::DEFAULT_ROTATION),
- _mass(EntityItem::DEFAULT_MASS),
- _velocity(EntityItem::DEFAULT_VELOCITY),
- _gravity(EntityItem::DEFAULT_GRAVITY),
- _damping(EntityItem::DEFAULT_DAMPING),
- _lifetime(EntityItem::DEFAULT_LIFETIME),
- _userData(EntityItem::DEFAULT_USER_DATA),
- _script(EntityItem::DEFAULT_SCRIPT),
- _registrationPoint(EntityItem::DEFAULT_REGISTRATION_POINT),
- _angularVelocity(EntityItem::DEFAULT_ANGULAR_VELOCITY),
- _angularDamping(EntityItem::DEFAULT_ANGULAR_DAMPING),
- _visible(EntityItem::DEFAULT_VISIBLE),
- _ignoreForCollisions(EntityItem::DEFAULT_IGNORE_FOR_COLLISIONS),
- _collisionsWillMove(EntityItem::DEFAULT_COLLISIONS_WILL_MOVE),
-
- _positionChanged(false),
- _dimensionsChanged(false),
- _rotationChanged(false),
- _massChanged(false),
- _velocityChanged(false),
- _gravityChanged(false),
- _dampingChanged(false),
- _lifetimeChanged(false),
- _userDataChanged(false),
- _scriptChanged(false),
- _registrationPointChanged(false),
- _angularVelocityChanged(false),
- _angularDampingChanged(false),
- _visibleChanged(false),
- _ignoreForCollisionsChanged(false),
- _collisionsWillMoveChanged(false),
-
- _color(),
- _modelURL(""),
- _animationURL(""),
- _animationIsPlaying(ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING),
- _animationFrameIndex(ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
- _animationFPS(ModelEntityItem::DEFAULT_ANIMATION_FPS),
- _animationSettings(""),
_glowLevel(0.0f),
_localRenderAlpha(1.0f),
- _isSpotlight(false),
-
- _colorChanged(false),
- _modelURLChanged(false),
- _animationURLChanged(false),
- _animationIsPlayingChanged(false),
- _animationFrameIndexChanged(false),
- _animationFPSChanged(false),
- _animationSettingsChanged(false),
_glowLevelChanged(false),
_localRenderAlphaChanged(false),
- _isSpotlightChanged(false),
-
- _diffuseColor(),
- _ambientColor(),
- _specularColor(),
- _constantAttenuation(1.0f),
- _linearAttenuation(0.0f),
- _quadraticAttenuation(0.0f),
- _exponent(0.0f),
- _cutoff(PI),
- _locked(false),
- _textures(""),
-
- _diffuseColorChanged(false),
- _ambientColorChanged(false),
- _specularColorChanged(false),
- _constantAttenuationChanged(false),
- _linearAttenuationChanged(false),
- _quadraticAttenuationChanged(false),
- _exponentChanged(false),
- _cutoffChanged(false),
- _lockedChanged(false),
- _texturesChanged(false),
_defaultSettings(true),
_naturalDimensions(1.0f, 1.0f, 1.0f)
@@ -226,6 +193,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_LOCKED, locked);
CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures);
CHECK_PROPERTY_CHANGE(PROP_USER_DATA, userData);
+ CHECK_PROPERTY_CHANGE(PROP_TEXT, text);
+ CHECK_PROPERTY_CHANGE(PROP_LINE_HEIGHT, lineHeight);
+ CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor);
+ CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_COLOR, backgroundColor);
return changedProperties;
}
@@ -280,6 +251,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
COPY_PROPERTY_TO_QSCRIPTVALUE(locked);
COPY_PROPERTY_TO_QSCRIPTVALUE(textures);
COPY_PROPERTY_TO_QSCRIPTVALUE(userData);
+ COPY_PROPERTY_TO_QSCRIPTVALUE(text);
+ COPY_PROPERTY_TO_QSCRIPTVALUE(lineHeight);
+ COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(textColor, getTextColor());
+ COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(backgroundColor, getBackgroundColor());
// Sitting properties support
QScriptValue sittingPoints = engine->newObject();
@@ -355,6 +330,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(locked, setLocked);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(textures, setTextures);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(userData, setUserData);
+ COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(text, setText);
+ COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(lineHeight, setLineHeight);
+ COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(textColor, setTextColor);
+ COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(backgroundColor, setBackgroundColor);
_lastEdited = usecTimestampNow();
}
@@ -494,30 +473,43 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, properties.getLifetime());
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, properties.getScript());
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, properties.getColor());
- APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL());
- APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL());
- APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS());
- APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex());
- APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, properties.getAnimationIsPlaying());
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, appendValue, properties.getRegistrationPoint());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, properties.getAngularVelocity());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, properties.getAngularDamping());
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, properties.getVisible());
APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, appendValue, properties.getIgnoreForCollisions());
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, properties.getCollisionsWillMove());
- APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, appendValue, properties.getIsSpotlight());
- APPEND_ENTITY_PROPERTY(PROP_DIFFUSE_COLOR, appendColor, properties.getDiffuseColor());
- APPEND_ENTITY_PROPERTY(PROP_AMBIENT_COLOR, appendColor, properties.getAmbientColor());
- APPEND_ENTITY_PROPERTY(PROP_SPECULAR_COLOR, appendColor, properties.getSpecularColor());
- APPEND_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, appendValue, properties.getConstantAttenuation());
- APPEND_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, appendValue, properties.getLinearAttenuation());
- APPEND_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, appendValue, properties.getQuadraticAttenuation());
- APPEND_ENTITY_PROPERTY(PROP_EXPONENT, appendValue, properties.getExponent());
- APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, properties.getCutoff());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, appendValue, properties.getLocked());
- APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, properties.getTextures());
- APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, properties.getAnimationSettings());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, appendValue, properties.getUserData());
+
+ if (properties.getType() == EntityTypes::Text) {
+ APPEND_ENTITY_PROPERTY(PROP_TEXT, appendValue, properties.getText());
+ APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, appendValue, properties.getLineHeight());
+ APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, appendColor, properties.getTextColor());
+ APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, appendColor, properties.getBackgroundColor());
+ }
+
+ if (properties.getType() == EntityTypes::Model) {
+ APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL());
+ APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL());
+ APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS());
+ APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex());
+ APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, properties.getAnimationIsPlaying());
+ APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, properties.getTextures());
+ APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, properties.getAnimationSettings());
+ }
+
+ if (properties.getType() == EntityTypes::Light) {
+ APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, appendValue, properties.getIsSpotlight());
+ APPEND_ENTITY_PROPERTY(PROP_DIFFUSE_COLOR, appendColor, properties.getDiffuseColor());
+ APPEND_ENTITY_PROPERTY(PROP_AMBIENT_COLOR, appendColor, properties.getAmbientColor());
+ APPEND_ENTITY_PROPERTY(PROP_SPECULAR_COLOR, appendColor, properties.getSpecularColor());
+ APPEND_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, appendValue, properties.getConstantAttenuation());
+ APPEND_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, appendValue, properties.getLinearAttenuation());
+ APPEND_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, appendValue, properties.getQuadraticAttenuation());
+ APPEND_ENTITY_PROPERTY(PROP_EXPONENT, appendValue, properties.getExponent());
+ APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, properties.getCutoff());
+ }
}
if (propertyCount > 0) {
int endOfEntityItemData = packetData->getUncompressedByteOffset();
@@ -705,30 +697,43 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_SCRIPT,setScript);
READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_COLOR, setColor);
- READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL);
- READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL);
- READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS);
- READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex);
- READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_FOR_COLLISIONS, bool, setIgnoreForCollisions);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONS_WILL_MOVE, bool, setCollisionsWillMove);
- READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_SPOTLIGHT, bool, setIsSpotlight);
- READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_DIFFUSE_COLOR, setDiffuseColor);
- READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_AMBIENT_COLOR, setAmbientColor);
- READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_SPECULAR_COLOR, setSpecularColor);
- READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CONSTANT_ATTENUATION, float, setConstantAttenuation);
- READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINEAR_ATTENUATION, float, setLinearAttenuation);
- READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_QUADRATIC_ATTENUATION, float, setQuadraticAttenuation);
- READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent);
- READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCKED, bool, setLocked);
- READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_TEXTURES, setTextures);
- READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_SETTINGS, setAnimationSettings);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_USER_DATA, setUserData);
+
+ if (properties.getType() == EntityTypes::Text) {
+ READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_TEXT, setText);
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight);
+ READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_TEXT_COLOR, setTextColor);
+ READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_BACKGROUND_COLOR, setBackgroundColor);
+ }
+
+ if (properties.getType() == EntityTypes::Model) {
+ READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL);
+ READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL);
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS);
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex);
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying);
+ READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_TEXTURES, setTextures);
+ READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_SETTINGS, setAnimationSettings);
+ }
+
+ if (properties.getType() == EntityTypes::Light) {
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_SPOTLIGHT, bool, setIsSpotlight);
+ READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_DIFFUSE_COLOR, setDiffuseColor);
+ READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_AMBIENT_COLOR, setAmbientColor);
+ READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_SPECULAR_COLOR, setSpecularColor);
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CONSTANT_ATTENUATION, float, setConstantAttenuation);
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINEAR_ATTENUATION, float, setLinearAttenuation);
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_QUADRATIC_ATTENUATION, float, setQuadraticAttenuation);
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent);
+ READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CUTOFF, float, setCutoff);
+ }
return valid;
}
@@ -800,6 +805,11 @@ void EntityItemProperties::markAllChanged() {
_cutoffChanged = true;
_lockedChanged = true;
_texturesChanged = true;
+
+ _textChanged = true;
+ _lineHeightChanged = true;
+ _textColorChanged = true;
+ _backgroundColorChanged = true;
}
AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const {
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index 273aedb18a..642e24beec 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -83,7 +83,14 @@ enum EntityPropertyList {
PROP_ANIMATION_SETTINGS,
PROP_USER_DATA,
- PROP_LAST_ITEM = PROP_USER_DATA
+ PROP_LAST_ITEM = PROP_USER_DATA,
+
+ // These properties of TextEntity piggy back off of properties of ModelEntities, the type doesn't matter
+ // since the derived class knows how to interpret it's own properties and knows the types it expects
+ PROP_TEXT_COLOR = PROP_COLOR,
+ PROP_TEXT = PROP_MODEL_URL,
+ PROP_LINE_HEIGHT = PROP_ANIMATION_URL,
+ PROP_BACKGROUND_COLOR = PROP_ANIMATION_FPS,
};
typedef PropertyFlags EntityPropertyFlags;
@@ -102,10 +109,14 @@ class EntityItemProperties {
friend class BoxEntityItem; // TODO: consider removing this friend relationship and use public methods
friend class SphereEntityItem; // TODO: consider removing this friend relationship and use public methods
friend class LightEntityItem; // TODO: consider removing this friend relationship and use public methods
+ friend class TextEntityItem; // TODO: consider removing this friend relationship and use public methods
public:
EntityItemProperties();
virtual ~EntityItemProperties();
+ EntityTypes::EntityType getType() const { return _type; }
+ void setType(EntityTypes::EntityType type) { _type = type; }
+
virtual QScriptValue copyToScriptValue(QScriptEngine* engine) const;
virtual void copyFromScriptValue(const QScriptValue& object);
@@ -123,86 +134,63 @@ public:
AABox getAABoxInMeters() const;
void debugDump() const;
+ void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; }
+ DEFINE_PROPERTY(PROP_VISIBLE, Visible, visible, bool);
+ DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3);
+ DEFINE_PROPERTY_REF(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3);
+ DEFINE_PROPERTY_REF(PROP_ROTATION, Rotation, rotation, glm::quat);
+ DEFINE_PROPERTY(PROP_MASS, Mass, mass, float);
+ DEFINE_PROPERTY_REF(PROP_VELOCITY, Velocity, velocity, glm::vec3);
+ DEFINE_PROPERTY_REF(PROP_GRAVITY, Gravity, gravity, glm::vec3);
+ DEFINE_PROPERTY(PROP_DAMPING, Damping, damping, float);
+ DEFINE_PROPERTY(PROP_LIFETIME, Lifetime, lifetime, float);
+ DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString);
+ DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor);
+ DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString);
+ DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, AnimationURL, animationURL, QString);
+ DEFINE_PROPERTY(PROP_ANIMATION_FPS, AnimationFPS, animationFPS, float);
+ DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, AnimationFrameIndex, animationFrameIndex, float);
+ DEFINE_PROPERTY(PROP_ANIMATION_PLAYING, AnimationIsPlaying, animationIsPlaying, bool);
+ DEFINE_PROPERTY_REF(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3);
+ DEFINE_PROPERTY_REF(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3);
+ DEFINE_PROPERTY(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float);
+ DEFINE_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, IgnoreForCollisions, ignoreForCollisions, bool);
+ DEFINE_PROPERTY(PROP_COLLISIONS_WILL_MOVE, CollisionsWillMove, collisionsWillMove, bool);
+ DEFINE_PROPERTY(PROP_IS_SPOTLIGHT, IsSpotlight, isSpotlight, bool);
+ DEFINE_PROPERTY_REF(PROP_DIFFUSE_COLOR, DiffuseColor, diffuseColor, xColor);
+ DEFINE_PROPERTY_REF(PROP_AMBIENT_COLOR, AmbientColor, ambientColor, xColor);
+ DEFINE_PROPERTY_REF(PROP_SPECULAR_COLOR, SpecularColor, specularColor, xColor);
+ DEFINE_PROPERTY(PROP_CONSTANT_ATTENUATION, ConstantAttenuation, constantAttenuation, float);
+ DEFINE_PROPERTY(PROP_LINEAR_ATTENUATION, LinearAttenuation, linearAttenuation, float);
+ DEFINE_PROPERTY(PROP_QUADRATIC_ATTENUATION, QuadraticAttenuation, quadraticAttenuation, float);
+ DEFINE_PROPERTY(PROP_EXPONENT, Exponent, exponent, float);
+ DEFINE_PROPERTY(PROP_CUTOFF, Cutoff, cutoff, float);
+ DEFINE_PROPERTY(PROP_LOCKED, Locked, locked, bool);
+ DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString);
+ DEFINE_PROPERTY_REF_WITH_SETTER_AND_GETTER(PROP_ANIMATION_SETTINGS, AnimationSettings, animationSettings, QString);
+ DEFINE_PROPERTY_REF(PROP_USER_DATA, UserData, userData, QString);
+ DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString);
+ DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float);
+ DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor);
+ DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor);
- // properties of all entities
- EntityTypes::EntityType getType() const { return _type; }
-
- void setType(EntityTypes::EntityType type) { _type = type; }
-
- const glm::vec3& getPosition() const { return _position; }
- /// set position in meter units, will be clamped to domain bounds
- void setPosition(const glm::vec3& value) { _position = glm::clamp(value, 0.0f, (float)TREE_SCALE); _positionChanged = true; }
-
-
- const glm::vec3& getDimensions() const { return _dimensions; }
- void setDimensions(const glm::vec3& value) { _dimensions = value; _dimensionsChanged = true; }
+public:
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }
- const glm::quat& getRotation() const { return _rotation; }
- void setRotation(const glm::quat& rotation) { _rotation = rotation; _rotationChanged = true; }
-
- float getMass() const { return _mass; }
- void setMass(float value) { _mass = value; _massChanged = true; }
-
- /// velocity in meters (0.0-1.0) per second
- const glm::vec3& getVelocity() const { return _velocity; }
- /// velocity in meters (0.0-1.0) per second
- void setVelocity(const glm::vec3& value) { _velocity = value; _velocityChanged = true; }
-
- /// gravity in meters (0.0-TREE_SCALE) per second squared
- const glm::vec3& getGravity() const { return _gravity; }
- /// gravity in meters (0.0-TREE_SCALE) per second squared
- void setGravity(const glm::vec3& value) { _gravity = value; _gravityChanged = true; }
-
- float getDamping() const { return _damping; }
- void setDamping(float value) { _damping = value; _dampingChanged = true; }
-
- float getLifetime() const { return _lifetime; } /// get the lifetime in seconds for the entity
- void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; } /// set the lifetime in seconds for the entity
-
- const QString& getUserData() const { return _userData; }
- void setUserData(const QString& value) { _userData = value; _userDataChanged = true; }
-
float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; }
quint64 getCreated() const { return _created; }
void setCreated(quint64 usecTime) { _created = usecTime; }
bool hasCreatedTime() const { return (_created != UNKNOWN_CREATED_TIME); }
-
- // NOTE: how do we handle _defaultSettings???
bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); }
bool containsPositionChange() const { return _positionChanged; }
bool containsDimensionsChange() const { return _dimensionsChanged; }
- // TODO: this need to be more generic. for now, we're going to have the properties class support these as
- // named getter/setters, but we want to move them to generic types...
- // properties we want to move to just models and particles
- xColor getColor() const { return _color; }
- const QString& getModelURL() const { return _modelURL; }
- const QString& getAnimationURL() const { return _animationURL; }
- float getAnimationFrameIndex() const { return _animationFrameIndex; }
- bool getAnimationIsPlaying() const { return _animationIsPlaying; }
- float getAnimationFPS() const { return _animationFPS; }
- QString getAnimationSettings() const;
-
float getGlowLevel() const { return _glowLevel; }
float getLocalRenderAlpha() const { return _localRenderAlpha; }
- const QString& getScript() const { return _script; }
-
- // model related properties
- void setColor(const xColor& value) { _color = value; _colorChanged = true; }
- void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; }
- void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; }
- void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; }
- void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; }
- void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; }
- void setAnimationSettings(const QString& value);
-
void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; }
void setLocalRenderAlpha(float value) { _localRenderAlpha = value; _localRenderAlphaChanged = true; }
- void setScript(const QString& value) { _script = value; _scriptChanged = true; }
-
static bool encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
unsigned char* bufferOut, int sizeIn, int& sizeOut);
@@ -214,23 +202,6 @@ public:
static bool decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes,
EntityItemID& entityID, EntityItemProperties& properties);
- bool positionChanged() const { return _positionChanged; }
- bool rotationChanged() const { return _rotationChanged; }
- bool massChanged() const { return _massChanged; }
- bool velocityChanged() const { return _velocityChanged; }
- bool gravityChanged() const { return _gravityChanged; }
- bool dampingChanged() const { return _dampingChanged; }
- bool lifetimeChanged() const { return _lifetimeChanged; }
- bool userDataChanged() const { return _userDataChanged; }
- bool scriptChanged() const { return _scriptChanged; }
- bool dimensionsChanged() const { return _dimensionsChanged; }
- bool registrationPointChanged() const { return _registrationPointChanged; }
- bool colorChanged() const { return _colorChanged; }
- bool modelURLChanged() const { return _modelURLChanged; }
- bool animationURLChanged() const { return _animationURLChanged; }
- bool animationIsPlayingChanged() const { return _animationIsPlayingChanged; }
- bool animationFrameIndexChanged() const { return _animationFrameIndexChanged; }
- bool animationFPSChanged() const { return _animationFPSChanged; }
bool glowLevelChanged() const { return _glowLevelChanged; }
bool localRenderAlphaChanged() const { return _localRenderAlphaChanged; }
@@ -238,161 +209,26 @@ public:
void markAllChanged();
void setSittingPoints(const QVector& sittingPoints);
-
+
const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; }
void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; }
- const glm::vec3& getRegistrationPoint() const { return _registrationPoint; }
- void setRegistrationPoint(const glm::vec3& value) { _registrationPoint = value; _registrationPointChanged = true; }
-
- const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
- void setAngularVelocity(const glm::vec3& value) { _angularVelocity = value; _angularVelocityChanged = true; }
-
- float getAngularDamping() const { return _angularDamping; }
- void setAngularDamping(float value) { _angularDamping = value; _angularDampingChanged = true; }
-
- bool getVisible() const { return _visible; }
- void setVisible(bool value) { _visible = value; _visibleChanged = true; }
-
- bool getIgnoreForCollisions() const { return _ignoreForCollisions; }
- void setIgnoreForCollisions(bool value) { _ignoreForCollisions = value; _ignoreForCollisionsChanged = true; }
-
- bool getCollisionsWillMove() const { return _collisionsWillMove; }
- void setCollisionsWillMove(bool value) { _collisionsWillMove = value; _collisionsWillMoveChanged = true; }
-
- bool getIsSpotlight() const { return _isSpotlight; }
- void setIsSpotlight(bool value) { _isSpotlight = value; _isSpotlightChanged = true; }
-
- xColor getDiffuseColor() const { return _diffuseColor; }
- xColor getAmbientColor() const { return _ambientColor; }
- xColor getSpecularColor() const { return _specularColor; }
-
- void setDiffuseColor(const xColor& value) { _diffuseColor = value; _diffuseColorChanged = true; }
- void setAmbientColor(const xColor& value) { _ambientColor = value; _ambientColorChanged = true; }
- void setSpecularColor(const xColor& value) { _specularColor = value; _specularColorChanged = true; }
-
- bool diffuseColorChanged() const { return _colorChanged; }
- bool ambientColorChanged() const { return _ambientColorChanged; }
- bool specularColorChanged() const { return _specularColorChanged; }
-
- float getConstantAttenuation() const { return _constantAttenuation; }
- void setConstantAttenuation(float value) { _constantAttenuation = value; _constantAttenuationChanged = true; }
-
- float getLinearAttenuation() const { return _linearAttenuation; }
- void setLinearAttenuation(float value) { _linearAttenuation = value; _linearAttenuationChanged = true; }
-
- float getQuadraticAttenuation() const { return _quadraticAttenuation; }
- void setQuadraticAttenuation(float value) { _quadraticAttenuation = value; _quadraticAttenuationChanged = true; }
-
- float getExponent() const { return _exponent; }
- void setExponent(float value) { _exponent = value; _exponentChanged = true; }
-
- float getCutoff() const { return _cutoff; }
- void setCutoff(float value) { _cutoff = value; _cutoffChanged = true; }
-
- bool getLocked() const { return _locked; }
- void setLocked(bool value) { _locked = value; _lockedChanged = true; }
- bool lockedChanged() const { return _lockedChanged; }
-
- const QString& getTextures() const { return _textures; }
- void setTextures(const QString& value) { _textures = value; _texturesChanged = true; }
-
const QStringList& getTextureNames() const { return _textureNames; }
void setTextureNames(const QStringList& value) { _textureNames = value; }
-
- void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; }
+
private:
-
QUuid _id;
bool _idSet;
quint64 _lastEdited;
quint64 _created;
-
EntityTypes::EntityType _type;
-
void setType(const QString& typeName) { _type = EntityTypes::getEntityTypeFromName(typeName); }
-
- glm::vec3 _position;
- glm::vec3 _dimensions;
- glm::quat _rotation;
- float _mass;
- glm::vec3 _velocity;
- glm::vec3 _gravity;
- float _damping;
- float _lifetime;
- QString _userData;
- QString _script;
- glm::vec3 _registrationPoint;
- glm::vec3 _angularVelocity;
- float _angularDamping;
- bool _visible;
- bool _ignoreForCollisions;
- bool _collisionsWillMove;
- bool _positionChanged;
- bool _dimensionsChanged;
- bool _rotationChanged;
- bool _massChanged;
- bool _velocityChanged;
- bool _gravityChanged;
- bool _dampingChanged;
- bool _lifetimeChanged;
- bool _userDataChanged;
- bool _scriptChanged;
- bool _registrationPointChanged;
- bool _angularVelocityChanged;
- bool _angularDampingChanged;
- bool _visibleChanged;
- bool _ignoreForCollisionsChanged;
- bool _collisionsWillMoveChanged;
-
- // TODO: this need to be more generic. for now, we're going to have the properties class support these as
- // named getter/setters, but we want to move them to generic types...
- xColor _color;
- QString _modelURL;
- QString _animationURL;
- bool _animationIsPlaying;
- float _animationFrameIndex;
- float _animationFPS;
- QString _animationSettings;
float _glowLevel;
float _localRenderAlpha;
- bool _isSpotlight;
-
- bool _colorChanged;
- bool _modelURLChanged;
- bool _animationURLChanged;
- bool _animationIsPlayingChanged;
- bool _animationFrameIndexChanged;
- bool _animationFPSChanged;
- bool _animationSettingsChanged;
bool _glowLevelChanged;
bool _localRenderAlphaChanged;
- bool _isSpotlightChanged;
-
- xColor _diffuseColor;
- xColor _ambientColor;
- xColor _specularColor;
- float _constantAttenuation;
- float _linearAttenuation;
- float _quadraticAttenuation;
- float _exponent;
- float _cutoff;
- bool _locked;
- QString _textures;
-
- bool _diffuseColorChanged;
- bool _ambientColorChanged;
- bool _specularColorChanged;
- bool _constantAttenuationChanged;
- bool _linearAttenuationChanged;
- bool _quadraticAttenuationChanged;
- bool _exponentChanged;
- bool _cutoffChanged;
- bool _lockedChanged;
- bool _texturesChanged;
-
bool _defaultSettings;
// NOTE: The following are pseudo client only properties. They are only used in clients which can access
@@ -406,6 +242,11 @@ QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const Enti
void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties);
+// define these inline here so the macros work
+inline void EntityItemProperties::setPosition(const glm::vec3& value)
+ { _position = glm::clamp(value, 0.0f, (float)TREE_SCALE); _positionChanged = true; }
+
+
inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
debug << "EntityItemProperties[" << "\n"
<< " position:" << properties.getPosition() << "in meters" << "\n"
diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h
index 77782cb90f..8ad444a975 100644
--- a/libraries/entities/src/EntityItemPropertiesMacros.h
+++ b/libraries/entities/src/EntityItemPropertiesMacros.h
@@ -260,6 +260,46 @@
} \
} \
}
+
+#define CONSTRUCT_PROPERTY(n, V) \
+ _##n(V), \
+ _##n##Changed(false)
+
+#define DEFINE_PROPERTY(P, N, n, T) \
+ public: \
+ T get##N() const { return _##n; } \
+ void set##N(T value) { _##n = value; _##n##Changed = true; } \
+ bool n##Changed() const { return _##n##Changed; } \
+ private: \
+ T _##n; \
+ bool _##n##Changed;
+
+#define DEFINE_PROPERTY_REF(P, N, n, T) \
+ public: \
+ const T& get##N() const { return _##n; } \
+ void set##N(const T& value) { _##n = value; _##n##Changed = true; } \
+ bool n##Changed() const { return _##n##Changed; } \
+ private: \
+ T _##n; \
+ bool _##n##Changed;
+
+#define DEFINE_PROPERTY_REF_WITH_SETTER(P, N, n, T) \
+ public: \
+ const T& get##N() const { return _##n; } \
+ void set##N(const T& value); \
+ bool n##Changed() const; \
+ private: \
+ T _##n; \
+ bool _##n##Changed;
+
+#define DEFINE_PROPERTY_REF_WITH_SETTER_AND_GETTER(P, N, n, T) \
+ public: \
+ T get##N() const; \
+ void set##N(const T& value); \
+ bool n##Changed() const; \
+ private: \
+ T _##n; \
+ bool _##n##Changed;
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index 1658764c71..29c4a8b19a 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -93,7 +93,6 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID
EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const EntityItemProperties& properties) {
EntityItemID actualID = entityID;
-
// if the entity is unknown, attempt to look it up
if (!entityID.isKnownID) {
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
@@ -113,6 +112,18 @@ EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const E
// if at this point, we know the id, send the update to the entity server
if (entityID.isKnownID) {
+ // make sure the properties has a type, so that the encode can know which properties to include
+ if (properties.getType() == EntityTypes::Unknown) {
+ EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
+ if (entity) {
+ EntityItemProperties tempProperties = properties;
+ tempProperties.setType(entity->getType());
+ queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, tempProperties);
+ return entityID;
+ }
+ }
+
+ // if the properties already includes the type, then use it as is
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
}
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index bc2823e15c..73957ad077 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -1089,7 +1089,6 @@ bool DebugOperator::preRecursion(OctreeElement* element) {
}
void EntityTree::dumpTree() {
- // First, look for the existing entity in the tree..
DebugOperator theOperator;
recurseTreeWithOperator(&theOperator);
}
@@ -1107,7 +1106,6 @@ bool PruneOperator::postRecursion(OctreeElement* element) {
}
void EntityTree::pruneTree() {
- // First, look for the existing entity in the tree..
PruneOperator theOperator;
recurseTreeWithOperator(&theOperator);
}
diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp
index aaa297f4fd..194df024e0 100644
--- a/libraries/entities/src/EntityTypes.cpp
+++ b/libraries/entities/src/EntityTypes.cpp
@@ -22,6 +22,7 @@
#include "LightEntityItem.h"
#include "ModelEntityItem.h"
#include "SphereEntityItem.h"
+#include "TextEntityItem.h"
QMap EntityTypes::_typeToNameMap;
QMap EntityTypes::_nameToTypeMap;
@@ -35,6 +36,7 @@ REGISTER_ENTITY_TYPE(Model)
REGISTER_ENTITY_TYPE(Box)
REGISTER_ENTITY_TYPE(Sphere)
REGISTER_ENTITY_TYPE(Light)
+REGISTER_ENTITY_TYPE(Text)
const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h
index 85bbff99ef..8ed407f11d 100644
--- a/libraries/entities/src/EntityTypes.h
+++ b/libraries/entities/src/EntityTypes.h
@@ -34,7 +34,8 @@ public:
Box,
Sphere,
Light,
- LAST = Light
+ Text,
+ LAST = Text
} EntityType;
static const QString& getEntityTypeName(EntityType entityType);
diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp
new file mode 100644
index 0000000000..491240c178
--- /dev/null
+++ b/libraries/entities/src/TextEntityItem.cpp
@@ -0,0 +1,113 @@
+//
+// TextEntityItem.cpp
+// libraries/entities/src
+//
+// Created by Brad Hefta-Gaub on 12/4/13.
+// Copyright 2013 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+
+#include
+
+#include
+
+#include "EntityTree.h"
+#include "EntityTreeElement.h"
+#include "TextEntityItem.h"
+
+
+const QString TextEntityItem::DEFAULT_TEXT("");
+const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f;
+const xColor TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 };
+const xColor TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0};
+
+EntityItem* TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
+ EntityItem* result = new TextEntityItem(entityID, properties);
+ return result;
+}
+
+TextEntityItem::TextEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
+ EntityItem(entityItemID)
+{
+ _type = EntityTypes::Text;
+ _created = properties.getCreated();
+ setProperties(properties, true);
+}
+
+EntityItemProperties TextEntityItem::getProperties() const {
+ EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
+
+ COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText);
+ COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight);
+ COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColorX);
+ COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColorX);
+ return properties;
+}
+
+bool TextEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
+ bool somethingChanged = false;
+ somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
+
+ SET_ENTITY_PROPERTY_FROM_PROPERTIES(text, setText);
+ SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight);
+ SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor);
+ SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundColor, setBackgroundColor);
+
+ if (somethingChanged) {
+ bool wantDebug = false;
+ if (wantDebug) {
+ uint64_t now = usecTimestampNow();
+ int elapsed = now - getLastEdited();
+ qDebug() << "TextEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
+ "now=" << now << " getLastEdited()=" << getLastEdited();
+ }
+ setLastEdited(properties._lastEdited);
+ }
+
+ return somethingChanged;
+}
+
+int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
+ ReadBitstreamToTreeParams& args,
+ EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
+
+ int bytesRead = 0;
+ const unsigned char* dataAt = data;
+
+ READ_ENTITY_PROPERTY_STRING(PROP_TEXT, setText);
+ READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, _lineHeight);
+ READ_ENTITY_PROPERTY_COLOR(PROP_TEXT_COLOR, _textColor);
+ READ_ENTITY_PROPERTY_COLOR(PROP_BACKGROUND_COLOR, _backgroundColor);
+
+ return bytesRead;
+}
+
+
+// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
+EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
+ EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
+ requestedProperties += PROP_TEXT;
+ requestedProperties += PROP_LINE_HEIGHT;
+ requestedProperties += PROP_TEXT_COLOR;
+ requestedProperties += PROP_BACKGROUND_COLOR;
+ return requestedProperties;
+}
+
+void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
+ EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
+ EntityPropertyFlags& requestedProperties,
+ EntityPropertyFlags& propertyFlags,
+ EntityPropertyFlags& propertiesDidntFit,
+ int& propertyCount,
+ OctreeElement::AppendState& appendState) const {
+
+ bool successPropertyFits = true;
+
+ APPEND_ENTITY_PROPERTY(PROP_TEXT, appendValue, getText());
+ APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, appendValue, getLineHeight());
+ APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, appendColor, getTextColor());
+ APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, appendColor, getBackgroundColor());
+}
\ No newline at end of file
diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h
new file mode 100644
index 0000000000..019d230c36
--- /dev/null
+++ b/libraries/entities/src/TextEntityItem.h
@@ -0,0 +1,81 @@
+//
+// TextEntityItem.h
+// libraries/entities/src
+//
+// Created by Brad Hefta-Gaub on 12/4/13.
+// Copyright 2013 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_TextEntityItem_h
+#define hifi_TextEntityItem_h
+
+#include "EntityItem.h"
+
+class TextEntityItem : public EntityItem {
+public:
+ static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
+
+ TextEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
+
+ ALLOW_INSTANTIATION // This class can be instantiated
+
+ // methods for getting/setting all properties of an entity
+ virtual EntityItemProperties getProperties() const;
+ virtual bool setProperties(const EntityItemProperties& properties, bool forceCopy = false);
+
+ // TODO: eventually only include properties changed since the params.lastViewFrustumSent time
+ virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
+
+ virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
+ EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
+ EntityPropertyFlags& requestedProperties,
+ EntityPropertyFlags& propertyFlags,
+ EntityPropertyFlags& propertiesDidntFit,
+ int& propertyCount,
+ OctreeElement::AppendState& appendState) const;
+
+ virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
+ ReadBitstreamToTreeParams& args,
+ EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
+
+ static const QString DEFAULT_TEXT;
+ void setText(const QString& value) { _text = value; }
+ const QString& getText() const { return _text; }
+
+ static const float DEFAULT_LINE_HEIGHT;
+ void setLineHeight(float value) { _lineHeight = value; }
+ float getLineHeight() const { return _lineHeight; }
+
+ static const xColor DEFAULT_TEXT_COLOR;
+ const rgbColor& getTextColor() const { return _textColor; }
+ xColor getTextColorX() const { xColor color = { _textColor[RED_INDEX], _textColor[GREEN_INDEX], _textColor[BLUE_INDEX] }; return color; }
+
+ void setTextColor(const rgbColor& value) { memcpy(_textColor, value, sizeof(_textColor)); }
+ void setTextColor(const xColor& value) {
+ _textColor[RED_INDEX] = value.red;
+ _textColor[GREEN_INDEX] = value.green;
+ _textColor[BLUE_INDEX] = value.blue;
+ }
+
+ static const xColor DEFAULT_BACKGROUND_COLOR;
+ const rgbColor& getBackgroundColor() const { return _backgroundColor; }
+ xColor getBackgroundColorX() const { xColor color = { _backgroundColor[RED_INDEX], _backgroundColor[GREEN_INDEX], _backgroundColor[BLUE_INDEX] }; return color; }
+
+ void setBackgroundColor(const rgbColor& value) { memcpy(_backgroundColor, value, sizeof(_backgroundColor)); }
+ void setBackgroundColor(const xColor& value) {
+ _backgroundColor[RED_INDEX] = value.red;
+ _backgroundColor[GREEN_INDEX] = value.green;
+ _backgroundColor[BLUE_INDEX] = value.blue;
+ }
+
+protected:
+ QString _text;
+ float _lineHeight;
+ rgbColor _textColor;
+ rgbColor _backgroundColor;
+};
+
+#endif // hifi_TextEntityItem_h
diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h
index d617f2243a..6cca5b505d 100644
--- a/libraries/networking/src/PacketHeaders.h
+++ b/libraries/networking/src/PacketHeaders.h
@@ -54,7 +54,7 @@ enum PacketType {
UNUSED_2,
UNUSED_3,
UNUSED_4,
- UNUSED_5,
+ PacketTypeNoisyMute,
PacketTypeMetavoxelData,
PacketTypeAvatarIdentity,
PacketTypeAvatarBillboard,
diff --git a/libraries/octree/src/OctreeHeadlessViewer.h b/libraries/octree/src/OctreeHeadlessViewer.h
index 03b8a204e0..9a6a5c7102 100644
--- a/libraries/octree/src/OctreeHeadlessViewer.h
+++ b/libraries/octree/src/OctreeHeadlessViewer.h
@@ -33,7 +33,8 @@ public:
virtual void renderElement(OctreeElement* element, RenderArgs* args) { /* swallow these */ }
virtual void init();
- virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE) { /* swallow these */ }
+ virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
+ RenderArgs::RenderSide renderSide = RenderArgs::MONO) { /* swallow these */ }
void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; }
diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp
index 406681c2c5..c9dcf82099 100644
--- a/libraries/octree/src/OctreePersistThread.cpp
+++ b/libraries/octree/src/OctreePersistThread.cpp
@@ -9,18 +9,38 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include
+
#include
+#include
+
#include
#include
#include "OctreePersistThread.h"
-OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, int persistInterval) :
+const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
+const int OctreePersistThread::DEFAULT_BACKUP_INTERVAL = 1000 * 60 * 30; // every 30 minutes
+const QString OctreePersistThread::DEFAULT_BACKUP_EXTENSION_FORMAT(".backup.%N");
+const int OctreePersistThread::DEFAULT_MAX_BACKUP_VERSIONS = 5;
+
+
+OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, int persistInterval,
+ bool wantBackup, int backupInterval, const QString& backupExtensionFormat,
+ int maxBackupVersions, bool debugTimestampNow) :
_tree(tree),
_filename(filename),
+ _backupExtensionFormat(backupExtensionFormat),
+ _maxBackupVersions(maxBackupVersions),
_persistInterval(persistInterval),
+ _backupInterval(backupInterval),
_initialLoadComplete(false),
- _loadTimeUSecs(0)
+ _loadTimeUSecs(0),
+ _lastCheck(0),
+ _lastBackup(0),
+ _wantBackup(wantBackup),
+ _debugTimestampNow(debugTimestampNow),
+ _lastTimeDebug(0)
{
}
@@ -51,16 +71,22 @@ bool OctreePersistThread::process() {
unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves", nodeCount, internalNodeCount, leafNodeCount);
- double usecPerGet = (double)OctreeElement::getGetChildAtIndexTime() / (double)OctreeElement::getGetChildAtIndexCalls();
- qDebug() << "getChildAtIndexCalls=" << OctreeElement::getGetChildAtIndexCalls()
- << " getChildAtIndexTime=" << OctreeElement::getGetChildAtIndexTime() << " perGet=" << usecPerGet;
+ bool wantDebug = false;
+ if (wantDebug) {
+ double usecPerGet = (double)OctreeElement::getGetChildAtIndexTime()
+ / (double)OctreeElement::getGetChildAtIndexCalls();
+ qDebug() << "getChildAtIndexCalls=" << OctreeElement::getGetChildAtIndexCalls()
+ << " getChildAtIndexTime=" << OctreeElement::getGetChildAtIndexTime() << " perGet=" << usecPerGet;
- double usecPerSet = (double)OctreeElement::getSetChildAtIndexTime() / (double)OctreeElement::getSetChildAtIndexCalls();
- qDebug() << "setChildAtIndexCalls=" << OctreeElement::getSetChildAtIndexCalls()
- << " setChildAtIndexTime=" << OctreeElement::getSetChildAtIndexTime() << " perset=" << usecPerSet;
+ double usecPerSet = (double)OctreeElement::getSetChildAtIndexTime()
+ / (double)OctreeElement::getSetChildAtIndexCalls();
+ qDebug() << "setChildAtIndexCalls=" << OctreeElement::getSetChildAtIndexCalls()
+ << " setChildAtIndexTime=" << OctreeElement::getSetChildAtIndexTime() << " perSet=" << usecPerSet;
+ }
_initialLoadComplete = true;
- _lastCheck = usecTimestampNow(); // we just loaded, no need to save again
+ _lastBackup = _lastCheck = usecTimestampNow(); // we just loaded, no need to save again
+ time(&_lastPersistTime);
emit loadCompleted();
}
@@ -78,19 +104,115 @@ bool OctreePersistThread::process() {
quint64 intervalToCheck = _persistInterval * MSECS_TO_USECS;
if (sinceLastSave > intervalToCheck) {
- // check the dirty bit and persist here...
- _lastCheck = usecTimestampNow();
- if (_tree->isDirty()) {
- qDebug() << "pruning Octree before saving...";
- _tree->pruneTree();
- qDebug() << "DONE pruning Octree before saving...";
-
- qDebug() << "saving Octree to file " << _filename << "...";
- _tree->writeToSVOFile(_filename.toLocal8Bit().constData());
- _tree->clearDirtyBit(); // tree is clean after saving
- qDebug("DONE saving Octree to file...");
- }
+ _lastCheck = now;
+ persist();
}
}
+
+ // if we were asked to debugTimestampNow do that now...
+ if (_debugTimestampNow) {
+ quint64 now = usecTimestampNow();
+ quint64 sinceLastDebug = now - _lastTimeDebug;
+ quint64 DEBUG_TIMESTAMP_INTERVAL = 600000000; // every 10 minutes
+
+ if (sinceLastDebug > DEBUG_TIMESTAMP_INTERVAL) {
+ _lastTimeDebug = usecTimestampNow(true); // ask for debug output
+ }
+
+ }
return isStillRunning(); // keep running till they terminate us
}
+
+
+void OctreePersistThread::aboutToFinish() {
+ qDebug() << "Persist thread about to finish...";
+ persist();
+ qDebug() << "Persist thread done with about to finish...";
+}
+
+void OctreePersistThread::persist() {
+ if (_tree->isDirty()) {
+ _tree->lockForWrite();
+ {
+ qDebug() << "pruning Octree before saving...";
+ _tree->pruneTree();
+ qDebug() << "DONE pruning Octree before saving...";
+ }
+ _tree->unlock();
+
+ backup(); // handle backup if requested
+
+ qDebug() << "saving Octree to file " << _filename << "...";
+ _tree->writeToSVOFile(qPrintable(_filename));
+ time(&_lastPersistTime);
+ _tree->clearDirtyBit(); // tree is clean after saving
+ qDebug() << "DONE saving Octree to file...";
+ }
+}
+
+void OctreePersistThread::rollOldBackupVersions() {
+ if (!_backupExtensionFormat.contains("%N")) {
+ return; // this backup extension format doesn't support rolling
+ }
+
+ qDebug() << "Rolling old backup versions...";
+ for(int n = _maxBackupVersions - 1; n > 0; n--) {
+ QString backupExtensionN = _backupExtensionFormat;
+ QString backupExtensionNplusOne = _backupExtensionFormat;
+ backupExtensionN.replace(QString("%N"), QString::number(n));
+ backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1));
+
+ QString backupFilenameN = _filename + backupExtensionN;
+ QString backupFilenameNplusOne = _filename + backupExtensionNplusOne;
+
+ QFile backupFileN(backupFilenameN);
+
+ if (backupFileN.exists()) {
+ qDebug() << "rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
+ int result = rename(qPrintable(backupFilenameN), qPrintable(backupFilenameNplusOne));
+ if (result == 0) {
+ qDebug() << "DONE rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
+ } else {
+ qDebug() << "ERROR in rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "...";
+ }
+ }
+ }
+ qDebug() << "Done rolling old backup versions...";
+}
+
+
+void OctreePersistThread::backup() {
+ if (_wantBackup) {
+ quint64 now = usecTimestampNow();
+ quint64 sinceLastBackup = now - _lastBackup;
+ quint64 MSECS_TO_USECS = 1000;
+ quint64 intervalToBackup = _backupInterval * MSECS_TO_USECS;
+
+ if (sinceLastBackup > intervalToBackup) {
+ struct tm* localTime = localtime(&_lastPersistTime);
+
+ QString backupFileName;
+
+ // check to see if they asked for version rolling format
+ if (_backupExtensionFormat.contains("%N")) {
+ rollOldBackupVersions(); // rename all the old backup files accordingly
+ QString backupExtension = _backupExtensionFormat;
+ backupExtension.replace(QString("%N"), QString("1"));
+ backupFileName = _filename + backupExtension;
+ } else {
+ char backupExtension[256];
+ strftime(backupExtension, sizeof(backupExtension), qPrintable(_backupExtensionFormat), localTime);
+ backupFileName = _filename + backupExtension;
+ }
+
+
+ qDebug() << "backing up persist file " << _filename << "to" << backupFileName << "...";
+ int result = rename(qPrintable(_filename), qPrintable(backupFileName));
+ if (result == 0) {
+ qDebug() << "DONE backing up persist file...";
+ } else {
+ qDebug() << "ERROR in backing up persist file...";
+ }
+ }
+ }
+}
diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h
index 2f86320b2e..b2093b89c0 100644
--- a/libraries/octree/src/OctreePersistThread.h
+++ b/libraries/octree/src/OctreePersistThread.h
@@ -22,27 +22,49 @@
class OctreePersistThread : public GenericThread {
Q_OBJECT
public:
- static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
+ static const int DEFAULT_PERSIST_INTERVAL;
+ static const int DEFAULT_BACKUP_INTERVAL;
+ static const QString DEFAULT_BACKUP_EXTENSION_FORMAT;
+ static const int DEFAULT_MAX_BACKUP_VERSIONS;
- OctreePersistThread(Octree* tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
+ OctreePersistThread(Octree* tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL,
+ bool wantBackup = false, int backupInterval = DEFAULT_BACKUP_INTERVAL,
+ const QString& backupExtensionFormat = DEFAULT_BACKUP_EXTENSION_FORMAT,
+ int maxBackupVersions = DEFAULT_MAX_BACKUP_VERSIONS,
+ bool debugTimestampNow = false);
bool isInitialLoadComplete() const { return _initialLoadComplete; }
quint64 getLoadElapsedTime() const { return _loadTimeUSecs; }
+ void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist
+
signals:
void loadCompleted();
protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
+
+ void persist();
+ void backup();
+ void rollOldBackupVersions();
private:
Octree* _tree;
QString _filename;
+ QString _backupExtensionFormat;
+ int _maxBackupVersions;
int _persistInterval;
+ int _backupInterval;
bool _initialLoadComplete;
quint64 _loadTimeUSecs;
quint64 _lastCheck;
+ quint64 _lastBackup;
+ bool _wantBackup;
+ time_t _lastPersistTime;
+
+ bool _debugTimestampNow;
+ quint64 _lastTimeDebug;
};
#endif // hifi_OctreePersistThread_h
diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h
index e8612b4cb6..2999f34fb6 100644
--- a/libraries/octree/src/OctreeRenderer.h
+++ b/libraries/octree/src/OctreeRenderer.h
@@ -52,7 +52,8 @@ public:
virtual void init();
/// render the content of the octree
- virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::RenderSide renderSide = RenderArgs::MONO);
+ virtual void render(RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
+ RenderArgs::RenderSide renderSide = RenderArgs::MONO);
ViewFrustum* getViewFrustum() const { return _viewFrustum; }
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; }
diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp
index 05d208c2f8..595d7078f8 100644
--- a/libraries/shared/src/SharedUtil.cpp
+++ b/libraries/shared/src/SharedUtil.cpp
@@ -37,7 +37,7 @@ void usecTimestampNowForceClockSkew(int clockSkew) {
::usecTimestampNowAdjust = clockSkew;
}
-quint64 usecTimestampNow() {
+quint64 usecTimestampNow(bool wantDebug) {
static bool usecTimestampNowIsInitialized = false;
static qint64 TIME_REFERENCE = 0; // in usec
static QElapsedTimer timestampTimer;
@@ -57,7 +57,6 @@ quint64 usecTimestampNow() {
quint64 msecsElapsed = timestampTimer.restart();
quint64 usecsElapsed = nsecsElapsed / 1000; // nsec to usec
TIME_REFERENCE += usecsElapsed;
- const bool wantDebug = false;
if (wantDebug) {
qDebug() << "usecTimestampNow() - resetting QElapsedTimer. ";
qDebug() << " RESET_AFTER_ELAPSED_NSECS:" << RESET_AFTER_ELAPSED_NSECS;
@@ -68,8 +67,34 @@ quint64 usecTimestampNow() {
}
}
- // usec nsec to usec usec
- return TIME_REFERENCE + timestampTimer.nsecsElapsed() / 1000 + ::usecTimestampNowAdjust;
+ quint64 nsecsElapsed = timestampTimer.nsecsElapsed();
+ quint64 usecsElapsed = nsecsElapsed / 1000; // nsec to usec
+ quint64 now = TIME_REFERENCE + usecsElapsed + ::usecTimestampNowAdjust;
+
+ if (wantDebug) {
+ QDateTime currentLocalTime = QDateTime::currentDateTime();
+
+ quint64 msecsNow = now / 1000; // usecs to msecs
+ QDateTime nowAsString;
+ nowAsString.setMSecsSinceEpoch(msecsNow);
+
+ quint64 msecsTimeReference = TIME_REFERENCE / 1000; // usecs to msecs
+ QDateTime timeReferenceAsString;
+ timeReferenceAsString.setMSecsSinceEpoch(msecsTimeReference);
+
+ qDebug() << "usecTimestampNow() - details... ";
+ qDebug() << " TIME_REFERENCE:" << TIME_REFERENCE;
+ qDebug() << " timeReferenceAsString:" << timeReferenceAsString.toString("yyyy-MM-dd hh:mm:ss.zzz");
+ qDebug() << " usecTimestampNowAdjust:" << usecTimestampNowAdjust;
+ qDebug() << " nsecsElapsed:" << nsecsElapsed;
+ qDebug() << " usecsElapsed:" << usecsElapsed;
+ qDebug() << " now:" << now;
+ qDebug() << " msecsNow:" << msecsNow;
+ qDebug() << " nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz");
+ qDebug() << " currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz");
+ }
+
+ return now;
}
float randFloat() {
diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h
index 69dfb4db35..3fe3c3d1ec 100644
--- a/libraries/shared/src/SharedUtil.h
+++ b/libraries/shared/src/SharedUtil.h
@@ -67,7 +67,7 @@ static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
const int BITS_IN_BYTE = 8;
-quint64 usecTimestampNow();
+quint64 usecTimestampNow(bool wantDebug = false);
void usecTimestampNowForceClockSkew(int clockSkew);
float randFloat();