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 @@ +
+ + + + +
+ + +
+
+ + + + +
+
+ + + + +
+
+ + + Red + Green + Blue + +
+
+ + + Red + Green + Blue + +
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();