diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 4f01fce125..69ad1cd815 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -8,6 +8,7 @@ include_glm() link_hifi_libraries( audio avatars octree voxels fbx entities metavoxels networking animation shared script-engine embedded-webserver + physics ) if (UNIX) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index b30cd355d1..e7ac7577b9 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include @@ -51,7 +51,7 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : connect(&_shutdownEventListener, SIGNAL(receivedCloseEvent()), SLOT(quit())); // set the logging target to the the CHILD_TARGET_NAME - Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); + LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); const QVariantMap argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); @@ -218,7 +218,7 @@ void AssignmentClient::handleAuthenticationRequest() { void AssignmentClient::assignmentCompleted() { // reset the logging target to the the CHILD_TARGET_NAME - Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); + LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); qDebug("Assignment finished or never started - waiting for new assignment."); diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index af09ff1535..02a4ff04f0 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include "AssignmentClientMonitor.h" @@ -21,7 +21,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int num QCoreApplication(argc, argv) { // start the Logging class with the parent's target name - Logging::setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME); + LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME); _childArguments = arguments(); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 8d87638434..6ca93a7b11 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -41,7 +41,7 @@ #include #include -#include +#include #include #include #include @@ -640,6 +640,7 @@ void AudioMixer::run() { timer.start(); char clientMixBuffer[MAX_PACKET_SIZE]; + char clientEnvBuffer[MAX_PACKET_SIZE]; int usecToSleep = BUFFER_SEND_INTERVAL_USECS; @@ -719,65 +720,90 @@ void AudioMixer::run() { int streamsMixed = prepareMixForListeningNode(node.data()); - char* dataAt; + char* mixDataAt; if (streamsMixed > 0) { // pack header - int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio); - dataAt = clientMixBuffer + numBytesPacketHeader; + int numBytesMixPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio); + mixDataAt = clientMixBuffer + numBytesMixPacketHeader; // pack sequence number quint16 sequence = nodeData->getOutgoingSequenceNumber(); - memcpy(dataAt, &sequence, sizeof(quint16)); - dataAt += sizeof(quint16); + memcpy(mixDataAt, &sequence, sizeof(quint16)); + mixDataAt += sizeof(quint16); + + // pack mixed audio samples + memcpy(mixDataAt, _mixSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO); + mixDataAt += NETWORK_BUFFER_LENGTH_BYTES_STEREO; - // Pack stream properties - bool inAZone = false; + // Send stream properties + bool hasReverb = false; + float reverbTime, wetLevel; + // find reverb properties for (int i = 0; i < _zoneReverbSettings.size(); ++i) { AudioMixerClientData* data = static_cast(node->getLinkedData()); glm::vec3 streamPosition = data->getAvatarAudioStream()->getPosition(); if (_audioZones[_zoneReverbSettings[i].zone].contains(streamPosition)) { - bool hasReverb = true; - float reverbTime = _zoneReverbSettings[i].reverbTime; - float wetLevel = _zoneReverbSettings[i].wetLevel; - - memcpy(dataAt, &hasReverb, sizeof(bool)); - dataAt += sizeof(bool); - memcpy(dataAt, &reverbTime, sizeof(float)); - dataAt += sizeof(float); - memcpy(dataAt, &wetLevel, sizeof(float)); - dataAt += sizeof(float); - - inAZone = true; + hasReverb = true; + reverbTime = _zoneReverbSettings[i].reverbTime; + wetLevel = _zoneReverbSettings[i].wetLevel; break; } } - if (!inAZone) { - bool hasReverb = false; - memcpy(dataAt, &hasReverb, sizeof(bool)); - dataAt += sizeof(bool); + AvatarAudioStream* stream = nodeData->getAvatarAudioStream(); + bool dataChanged = (stream->hasReverb() != hasReverb) || + (stream->hasReverb() && (stream->getRevebTime() != reverbTime || + stream->getWetLevel() != wetLevel)); + if (dataChanged) { + // Update stream + if (hasReverb) { + stream->setReverb(reverbTime, wetLevel); + } else { + stream->clearReverb(); + } } - // pack mixed audio samples - memcpy(dataAt, _mixSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO); - dataAt += NETWORK_BUFFER_LENGTH_BYTES_STEREO; + // Send at change or every so often + float CHANCE_OF_SEND = 0.01f; + bool sendData = dataChanged || (randFloat() < CHANCE_OF_SEND); + + if (sendData) { + int numBytesEnvPacketHeader = populatePacketHeader(clientEnvBuffer, PacketTypeAudioEnvironment); + char* envDataAt = clientEnvBuffer + numBytesEnvPacketHeader; + + unsigned char bitset = 0; + if (hasReverb) { + setAtBit(bitset, HAS_REVERB_BIT); + } + + memcpy(envDataAt, &bitset, sizeof(unsigned char)); + envDataAt += sizeof(unsigned char); + + if (hasReverb) { + memcpy(envDataAt, &reverbTime, sizeof(float)); + envDataAt += sizeof(float); + memcpy(envDataAt, &wetLevel, sizeof(float)); + envDataAt += sizeof(float); + } + nodeList->writeDatagram(clientEnvBuffer, envDataAt - clientEnvBuffer, node); + } } else { // pack header int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeSilentAudioFrame); - dataAt = clientMixBuffer + numBytesPacketHeader; + mixDataAt = clientMixBuffer + numBytesPacketHeader; // pack sequence number quint16 sequence = nodeData->getOutgoingSequenceNumber(); - memcpy(dataAt, &sequence, sizeof(quint16)); - dataAt += sizeof(quint16); + memcpy(mixDataAt, &sequence, sizeof(quint16)); + mixDataAt += sizeof(quint16); // pack number of silent audio samples quint16 numSilentSamples = NETWORK_BUFFER_LENGTH_SAMPLES_STEREO; - memcpy(dataAt, &numSilentSamples, sizeof(quint16)); - dataAt += sizeof(quint16); + memcpy(mixDataAt, &numSilentSamples, sizeof(quint16)); + mixDataAt += sizeof(quint16); } // send mixed audio packet - nodeList->writeDatagram(clientMixBuffer, dataAt - clientMixBuffer, node); + nodeList->writeDatagram(clientMixBuffer, mixDataAt - clientMixBuffer, node); nodeData->incrementOutgoingMixedAudioSequenceNumber(); // send an audio stream stats packet if it's time diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 93f38e3608..3ec7c5cfbf 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include #include diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 5e103cf767..3bf6990a74 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include #include #include "Assignment.h" @@ -22,7 +22,7 @@ int main(int argc, char* argv[]) { #endif // use the verbose message handler in Logging - qInstallMessageHandler(Logging::verboseMessageHandler); + qInstallMessageHandler(LogHandler::verboseMessageHandler); const char* numForksString = getCmdOption(argc, (const char**)argv, NUM_FORKS_PARAMETER); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index d0a17287cb..121cac0273 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include "../AssignmentClient.h" @@ -923,7 +923,7 @@ void OctreeServer::run() { beforeRun(); // after payload has been processed - qInstallMessageHandler(Logging::verboseMessageHandler); + qInstallMessageHandler(LogHandler::verboseMessageHandler); const char* STATUS_PORT = "--statusPort"; const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT); diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp index 82e4bf7cab..ba80e6fce0 100644 --- a/domain-server/src/main.cpp +++ b/domain-server/src/main.cpp @@ -17,7 +17,7 @@ #include -#include +#include #include #include "DomainServer.h" @@ -27,7 +27,7 @@ int main(int argc, char* argv[]) { setvbuf(stdout, NULL, _IOLBF, 0); #endif - qInstallMessageHandler(Logging::verboseMessageHandler); + qInstallMessageHandler(LogHandler::verboseMessageHandler); int currentExitCode = 0; diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index becc8c86b2..c23f8b0dcc 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -143,6 +143,41 @@ EntityPropertyDialogBox = (function () { array.push({ label: "Blue:", value: properties.color.blue }); index++; } + + if (properties.type == "Light") { + array.push({ label: "Light Properties:", type: "header" }); + index++; + array.push({ label: "Is Spot Light:", value: properties.isSpotlight }); + index++; + array.push({ label: "Diffuse Red:", value: properties.diffuseColor.red }); + index++; + array.push({ label: "Diffuse Green:", value: properties.diffuseColor.green }); + index++; + array.push({ label: "Diffuse Blue:", value: properties.diffuseColor.blue }); + index++; + array.push({ label: "Ambient Red:", value: properties.ambientColor.red }); + index++; + array.push({ label: "Ambient Green:", value: properties.ambientColor.green }); + index++; + array.push({ label: "Ambient Blue:", value: properties.ambientColor.blue }); + index++; + array.push({ label: "Specular Red:", value: properties.specularColor.red }); + index++; + array.push({ label: "Specular Green:", value: properties.specularColor.green }); + index++; + array.push({ label: "Specular Blue:", value: properties.specularColor.blue }); + index++; + array.push({ label: "Constant Attenuation:", value: properties.constantAttenuation }); + index++; + array.push({ label: "Linear Attenuation:", value: properties.linearAttenuation }); + index++; + array.push({ label: "Quadratic Attenuation:", value: properties.quadraticAttenuation }); + index++; + array.push({ label: "Exponent:", value: properties.exponent }); + index++; + array.push({ label: "Cutoff (in degrees):", value: properties.cutoff }); + index++; + } array.push({ button: "Cancel" }); index++; @@ -243,6 +278,25 @@ EntityPropertyDialogBox = (function () { properties.color.green = array[index++].value; properties.color.blue = array[index++].value; } + if (properties.type == "Light") { + index++; // skip header + properties.isSpotlight = array[index++].value; + properties.diffuseColor.red = array[index++].value; + properties.diffuseColor.green = array[index++].value; + properties.diffuseColor.blue = array[index++].value; + properties.ambientColor.red = array[index++].value; + properties.ambientColor.green = array[index++].value; + properties.ambientColor.blue = array[index++].value; + properties.specularColor.red = array[index++].value; + properties.specularColor.green = array[index++].value; + properties.specularColor.blue = array[index++].value; + properties.constantAttenuation = array[index++].value; + properties.linearAttenuation = array[index++].value; + properties.quadraticAttenuation = array[index++].value; + properties.exponent = array[index++].value; + properties.cutoff = array[index++].value; + } + Entities.editEntity(editModelID, properties); selectionDisplay.select(editModelID, false); } diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index c503ab82a4..a325491dd3 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -19,10 +19,15 @@ SPACE_WORLD = "world"; SelectionManager = (function() { var that = {}; + var PENDING_SELECTION_CHECK_INTERVAL = 50; + that.savedProperties = {}; that.eventListener = null; that.selections = []; + // These are selections that don't have a known ID yet + that.pendingSelections = []; + var pendingSelectionTimer = null; that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); that.localPosition = { x: 0, y: 0, z: 0 }; @@ -45,11 +50,58 @@ SelectionManager = (function() { that.eventListener = func; }; + that.hasSelection = function() { + return that.selections.length > 0; + }; + + that.setSelections = function(entityIDs) { + that.selections = []; + for (var i = 0; i < entityIDs.length; i++) { + var entityID = entityIDs[i]; + if (entityID.isKnownID) { + that.selections.push(entityID); + } else { + that.pendingSelections.push(entityID); + if (pendingSelectionTimer == null) { + pendingSelectionTimer = Script.setInterval(that._checkPendingSelections, PENDING_SELECTION_CHECK_INTERVAL); + } + } + } + + that._update(); + }; + + that._checkPendingSelections = function() { + for (var i = 0; i < that.pendingSelections.length; i++) { + var entityID = that.pendingSelections[i]; + var newEntityID = Entities.identifyEntity(entityID); + if (newEntityID.isKnownID) { + that.pendingSelections.splice(i, 1); + that.addEntity(newEntityID); + i--; + } + } + + if (that.pendingSelections.length == 0) { + Script.clearInterval(pendingSelectionTimer); + pendingSelectionTimer = null; + } + } + that.addEntity = function(entityID) { - print("Adding: " + entityID.id); - var idx = that.selections.indexOf(entityID); - if (idx == -1) { - that.selections.push(entityID); + if (entityID.isKnownID) { + var idx = that.selections.indexOf(entityID); + if (idx == -1) { + that.selections.push(entityID); + } + } else { + var idx = that.pendingSelections.indexOf(entityID); + if (idx == -1) { + that.pendingSelections.push(entityID); + if (pendingSelectionTimer == null) { + pendingSelectionTimer = Script.setInterval(that._checkPendingSelections, PENDING_SELECTION_CHECK_INTERVAL); + } + } } that._update(); @@ -61,11 +113,22 @@ SelectionManager = (function() { that.selections.splice(idx, 1); } + var idx = that.pendingSelections.indexOf(entityID); + if (idx >= 0) { + that.pendingSelections.splice(idx, 1); + } + that._update(); }; that.clearSelections = function() { that.selections = []; + that.pendingSelections = []; + + if (pendingSelectionTimer !== null) { + Script.clearInterval(pendingSelectionTimer); + pendingSelectionTimer = null; + } that._update(); }; @@ -77,8 +140,6 @@ SelectionManager = (function() { that.worldDimensions = null; that.worldPosition = null; } else if (that.selections.length == 1) { - SelectionDisplay.setSpaceMode(SPACE_LOCAL); - var properties = Entities.getEntityProperties(that.selections[0]); that.localDimensions = properties.dimensions; that.localPosition = properties.position; @@ -86,10 +147,9 @@ SelectionManager = (function() { that.worldDimensions = properties.boundingBox.dimensions; that.worldPosition = properties.boundingBox.center; - } else { - // For 1+ selections we can only modify selections in world space - SelectionDisplay.setSpaceMode(SPACE_WORLD); + SelectionDisplay.setSpaceMode(SPACE_LOCAL); + } else { that.localRotation = null; that.localDimensions = null; that.localPosition = null; @@ -122,6 +182,9 @@ SelectionManager = (function() { y: brn.y + (that.worldDimensions.y / 2), z: brn.z + (that.worldDimensions.z / 2), }; + + // For 1+ selections we can only modify selections in world space + SelectionDisplay.setSpaceMode(SPACE_WORLD); } if (that.eventListener) { @@ -600,7 +663,7 @@ SelectionDisplay = (function () { currentSelection = entityID; entitySelected = true; - lastCameraPosition = Camera.getPosition(); + // lastCameraPosition = Camera.getPosition(); lastCameraOrientation = Camera.getOrientation(); if (event !== false) { @@ -623,10 +686,15 @@ SelectionDisplay = (function () { } + Entities.editEntity(entityID, { localRenderAlpha: 0.1 }); + Overlays.editOverlay(highlightBox, { visible: false }); + + that.updateHandles(); + } + + that.updateRotationHandles = function() { var diagonal = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1; var halfDimensions = Vec3.multiply(selectionManager.worldDimensions, 0.5); - innerRadius = diagonal; - outerRadius = diagonal * 1.15; var innerActive = false; var innerAlpha = 0.2; var outerAlpha = 0.2; @@ -637,22 +705,18 @@ SelectionDisplay = (function () { } var rotateHandleOffset = 0.05; - var grabberMoveUpOffset = 0.1; var top, far, left, bottom, near, right, boundsCenter, objectCenter, BLN, BRN, BLF, TLN, TRN, TLF, TRF; - // objectCenter = { x: properties.position.x, y: properties.position.y, z: properties.position.z }; - - var dimensions; + var dimensions, rotation; if (spaceMode == SPACE_LOCAL) { - objectCenter = SelectionManager.localPosition; - dimensions = SelectionManager.localDimensions; + rotation = SelectionManager.localRotation; } else { - objectCenter = SelectionManager.worldPosition; - dimensions = SelectionManager.worldDimensions; + rotation = SelectionManager.worldRotation; } objectCenter = SelectionManager.worldPosition; dimensions = SelectionManager.worldDimensions; + var position = objectCenter; top = objectCenter.y + (dimensions.y / 2); far = objectCenter.z + (dimensions.z / 2); @@ -662,7 +726,6 @@ SelectionDisplay = (function () { near = objectCenter.z - (dimensions.z / 2); right = objectCenter.x - (dimensions.x / 2); - // boundsCenter = { x: properties.boundingBox.center.x, y: properties.boundingBox.center.y, z: properties.boundingBox.center.z }; boundsCenter = objectCenter; var yawCorner; @@ -812,10 +875,12 @@ SelectionDisplay = (function () { } var rotateHandlesVisible = true; + var rotationOverlaysVisible = false; var translateHandlesVisible = true; var stretchHandlesVisible = true; var selectionBoxVisible = true; if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL" || mode == "TRANSLATE_X in case they Z") { + rotationOverlaysVisible = true; rotateHandlesVisible = false; translateHandlesVisible = false; stretchHandlesVisible = false; @@ -828,77 +893,33 @@ SelectionDisplay = (function () { rotateHandlesVisible = false; translateHandlesVisible = false; } - - Overlays.editOverlay(highlightBox, { visible: false }); - var dimensions, rotation, position; - if (spaceMode == SPACE_LOCAL) { - rotation = SelectionManager.localRotation; - dimensions = SelectionManager.localDimensions; - position = SelectionManager.localPosition; - } else { - rotation = SelectionManager.worldRotation; - dimensions = SelectionManager.worldDimensions; - position = SelectionManager.worldPosition; - } - - Overlays.editOverlay(grabberMoveUp, { visible: translateHandlesVisible, position: { x: boundsCenter.x, y: top + grabberMoveUpOffset, z: boundsCenter.z } }); - - that.updateHandles(); - + var rotation = SelectionManager.worldRotation; + var dimensions = SelectionManager.worldDimensions; + var position = SelectionManager.worldPosition; Overlays.editOverlay(baseOfEntityProjectionOverlay, { visible: true, solid:true, lineWidth: 2.0, - position: { x: properties.position.x, + position: { x: position.x, y: 0, - z: properties.position.z }, + z: position.z }, - dimensions: { x: properties.dimensions.x, y: properties.dimensions.z }, - rotation: properties.rotation, + dimensions: { x: dimensions.x, y: 0, z: dimensions.z }, + rotation: rotation, }); - Overlays.editOverlay(rotateOverlayTarget, { visible: false }); - - Overlays.editOverlay(rotateOverlayInner, - { - visible: false, - size: innerRadius, - innerRadius: 0.9, - alpha: innerAlpha - }); - - Overlays.editOverlay(rotateOverlayOuter, - { - visible: false, - size: outerRadius, - innerRadius: 0.9, - startAt: 0, - endAt: 360, - alpha: outerAlpha, - }); - - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: false, - size: outerRadius, - startAt: 0, - endAt: 0, - innerRadius: 0.9, - }); - - Overlays.editOverlay(rotateZeroOverlay, { visible: false }); - Overlays.editOverlay(rotateCurrentOverlay, { visible: false }); + Overlays.editOverlay(rotateOverlayTarget, { visible: rotationOverlaysVisible }); + Overlays.editOverlay(rotateZeroOverlay, { visible: rotationOverlaysVisible }); + Overlays.editOverlay(rotateCurrentOverlay, { visible: rotationOverlaysVisible }); // TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden Overlays.editOverlay(yawHandle, { visible: rotateHandlesVisible, position: yawCorner, rotation: yawHandleRotation}); Overlays.editOverlay(pitchHandle, { visible: rotateHandlesVisible, position: pitchCorner, rotation: pitchHandleRotation}); Overlays.editOverlay(rollHandle, { visible: rotateHandlesVisible, position: rollCorner, rotation: rollHandleRotation}); - - Entities.editEntity(entityID, { localRenderAlpha: 0.1 }); }; that.setSpaceMode = function(newSpaceMode) { @@ -931,6 +952,8 @@ SelectionDisplay = (function () { return; } + that.updateRotationHandles(); + var rotation, dimensions, position; if (spaceMode == SPACE_LOCAL) { @@ -945,12 +968,14 @@ SelectionDisplay = (function () { var halfDimensions = Vec3.multiply(0.5, dimensions); - left = -halfDimensions.x; - right = halfDimensions.x; - top = halfDimensions.y; - bottom = -halfDimensions.y; + var left = -halfDimensions.x; + var right = halfDimensions.x; + var top = halfDimensions.y; + var bottom = -halfDimensions.y; var front = far = halfDimensions.z; - near = -halfDimensions.z; + var near = -halfDimensions.z; + + var worldTop = SelectionManager.worldDimensions.y / 2; var LBN = { x: left, y: bottom, z: near }; var RBN = { x: right, y: bottom, z: near }; @@ -1060,7 +1085,7 @@ SelectionDisplay = (function () { position: position, dimensions: dimensions, rotation: rotation, - visible: true, + visible: !(mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL"), }); Overlays.editOverlay(grabberEdgeTR, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeTR }); @@ -1076,6 +1101,8 @@ SelectionDisplay = (function () { Overlays.editOverlay(grabberEdgeFR, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeFR }); Overlays.editOverlay(grabberEdgeFL, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeFL }); + var grabberMoveUpOffset = 0.1; + Overlays.editOverlay(grabberMoveUp, { visible: activeTool == null || mode == "TRANSLATE_UP_DOWN", position: { x: position.x, y: position.y + worldTop + grabberMoveUpOffset, z: position.z } }); }; that.setOverlaysVisible = function(isVisible) { @@ -1094,47 +1121,10 @@ SelectionDisplay = (function () { entitySelected = false; }; - function applyEntityProperties(data) { - for (var i = 0; i < data.length; i++) { - var entityID = data[i].entityID; - var properties = data[i].properties; - Entities.editEntity(entityID, properties); - } - selectionManager._update(); - }; - - // For currently selected entities, push a command to the UndoStack that uses the current entity properties for the - // redo command, and the saved properties for the undo command. - function pushCommandForSelections() { - var undoData = []; - var redoData = []; - for (var i = 0; i < SelectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID.id]; - var currentProperties = Entities.getEntityProperties(entityID); - undoData.push({ - entityID: entityID, - properties: { - position: initialProperties.position, - rotation: initialProperties.rotation, - dimensions: initialProperties.dimensions, - }, - }); - redoData.push({ - entityID: entityID, - properties: { - position: currentProperties.position, - rotation: currentProperties.rotation, - dimensions: currentProperties.dimensions, - }, - }); - } - UndoStack.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData); - } - var initialXZPick = null; var isConstrained = false; var startPosition = null; + var duplicatedEntityIDs = null; var translateXZTool = { mode: 'TRANSLATE_XZ', onBegin: function(event) { @@ -1149,43 +1139,32 @@ SelectionDisplay = (function () { // copy of the selected entities and move the _original_ entities, not // the new ones. if (event.isAlt) { + duplicatedEntityIDs = []; for (var otherEntityID in SelectionManager.savedProperties) { var properties = SelectionManager.savedProperties[otherEntityID]; var entityID = Entities.addEntity(properties); + duplicatedEntityIDs.push({ + entityID: entityID, + properties: properties, + }); } + } else { + duplicatedEntityIDs = null; } isConstrained = false; }, onEnd: function(event, reason) { - if (reason == 'cancel') { - for (var i = 0; i < SelectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID.id]; - Entities.editEntity(entityID, initialProperties); - } - } else { - pushCommandForSelections(); - } + pushCommandForSelections(duplicatedEntityIDs); + Overlays.editOverlay(xRailOverlay, { visible: false }); Overlays.editOverlay(zRailOverlay, { visible: false }); }, onMove: function(event) { - if (!entitySelected || mode !== "TRANSLATE_XZ") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastCameraOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { x: 0, y: 1, z: 0 }); var vector = Vec3.subtract(pick, initialXZPick); - // initialXZPick = pick; // If shifted, constrain to one axis if (event.isShifted) { @@ -1222,7 +1201,6 @@ SelectionDisplay = (function () { if (wantDebug) { print("translateXZ... "); Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); Vec3.print(" vector:", vector); Vec3.print(" newPosition:", properties.position); Vec3.print(" newPosition:", newPosition); @@ -1239,22 +1217,28 @@ SelectionDisplay = (function () { mode: "TRANSLATE_UP_DOWN", onBegin: function(event) { SelectionManager.saveProperties(); - }, - onEnd: function(event, reason) { - if (reason == 'cancel') { - for (var i = 0; i < SelectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID.id]; - Entities.editEntity(entityID, initialProperties); + + // Duplicate entities if alt is pressed. This will make a + // copy of the selected entities and move the _original_ entities, not + // the new ones. + if (event.isAlt) { + duplicatedEntityIDs = []; + for (var otherEntityID in SelectionManager.savedProperties) { + var properties = SelectionManager.savedProperties[otherEntityID]; + var entityID = Entities.addEntity(properties); + duplicatedEntityIDs.push({ + entityID: entityID, + properties: properties, + }); } } else { - pushCommandForSelections(); + duplicatedEntityIDs = null; } }, + onEnd: function(event, reason) { + pushCommandForSelections(duplicatedEntityIDs); + }, onMove: function(event) { - if (!entitySelected || mode !== "TRANSLATE_UP_DOWN") { - return; // not allowed - } pickRay = Camera.computePickRay(event.x, event.y); // translate mode left/right based on view toward entity @@ -1403,21 +1387,10 @@ SelectionDisplay = (function () { Overlays.editOverlay(yRailOverlay, { visible: false }); Overlays.editOverlay(zRailOverlay, { visible: false }); - if (reason == 'cancel') { - for (var i = 0; i < SelectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID.id]; - Entities.editEntity(entityID, initialProperties); - } - } else { - pushCommandForSelections(); - } + pushCommandForSelections(); }; var onMove = function(event) { - if (!entitySelected || mode !== stretchMode) { - return; // not allowed - } var proportional = spaceMode == SPACE_WORLD || event.isShifted; var position, dimensions, rotation; @@ -1510,14 +1483,6 @@ SelectionDisplay = (function () { }; }; - that.cancelTool = function() { - if (activeTool) { - activeTool.onEnd(null, 'cancel'); - activeTool = null; - SelectionManager._update(); - } - }; - function addStretchTool(overlay, mode, pivot, direction) { if (!pivot) { pivot = Vec3.multiply(-1, direction); @@ -1563,23 +1528,49 @@ SelectionDisplay = (function () { onBegin: function(event) { SelectionManager.saveProperties(); initialPosition = SelectionManager.worldPosition; + + // Size the overlays to the current selection size + var diagonal = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1; + var halfDimensions = Vec3.multiply(selectionManager.worldDimensions, 0.5); + innerRadius = diagonal; + outerRadius = diagonal * 1.15; + var innerAlpha = 0.2; + var outerAlpha = 0.2; + Overlays.editOverlay(rotateOverlayInner, + { + visible: true, + size: innerRadius, + innerRadius: 0.9, + alpha: innerAlpha + }); + + Overlays.editOverlay(rotateOverlayOuter, + { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); + + Overlays.editOverlay(rotateOverlayCurrent, + { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); }, onEnd: function(event, reason) { - if (reason == 'cancel') { - for (var i = 0; i < SelectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID.id]; - Entities.editEntity(entityID, initialProperties); - } - } else { - pushCommandForSelections(); - } + Overlays.editOverlay(rotateOverlayInner, { visible: false }); + Overlays.editOverlay(rotateOverlayOuter, { visible: false }); + Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + + pushCommandForSelections(); }, onMove: function(event) { - if (!entitySelected || mode !== "ROTATE_YAW") { - return; // not allowed - } - var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); if (debug) { @@ -1591,9 +1582,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); @@ -1611,6 +1599,7 @@ SelectionDisplay = (function () { 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; @@ -1671,22 +1660,49 @@ SelectionDisplay = (function () { onBegin: function(event) { SelectionManager.saveProperties(); initialPosition = SelectionManager.worldPosition; + + // Size the overlays to the current selection size + var diagonal = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1; + var halfDimensions = Vec3.multiply(selectionManager.worldDimensions, 0.5); + var innerRadius = diagonal; + var outerRadius = diagonal * 1.15; + var innerAlpha = 0.2; + var outerAlpha = 0.2; + Overlays.editOverlay(rotateOverlayInner, + { + visible: true, + size: innerRadius, + innerRadius: 0.9, + alpha: innerAlpha + }); + + Overlays.editOverlay(rotateOverlayOuter, + { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); + + Overlays.editOverlay(rotateOverlayCurrent, + { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); }, onEnd: function(event, reason) { - if (reason == 'cancel') { - for (var i = 0; i < SelectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID.id]; - Entities.editEntity(entityID, initialProperties); - } - } else { - pushCommandForSelections(); - } + Overlays.editOverlay(rotateOverlayInner, { visible: false }); + Overlays.editOverlay(rotateOverlayOuter, { visible: false }); + Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + + pushCommandForSelections(); }, onMove: function(event) { - if (!entitySelected || mode !== "ROTATE_PITCH") { - return; // not allowed - } var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); if (debug) { @@ -1698,9 +1714,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (debug) { @@ -1777,22 +1790,49 @@ SelectionDisplay = (function () { onBegin: function(event) { SelectionManager.saveProperties(); initialPosition = SelectionManager.worldPosition; + + // Size the overlays to the current selection size + var diagonal = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1; + var halfDimensions = Vec3.multiply(selectionManager.worldDimensions, 0.5); + var innerRadius = diagonal; + var outerRadius = diagonal * 1.15; + var innerAlpha = 0.2; + var outerAlpha = 0.2; + Overlays.editOverlay(rotateOverlayInner, + { + visible: true, + size: innerRadius, + innerRadius: 0.9, + alpha: innerAlpha + }); + + Overlays.editOverlay(rotateOverlayOuter, + { + visible: true, + size: outerRadius, + innerRadius: 0.9, + startAt: 0, + endAt: 360, + alpha: outerAlpha, + }); + + Overlays.editOverlay(rotateOverlayCurrent, + { + visible: true, + size: outerRadius, + startAt: 0, + endAt: 0, + innerRadius: 0.9, + }); }, onEnd: function(event, reason) { - if (reason == 'cancel') { - for (var i = 0; i < SelectionManager.selections.length; i++) { - var entityID = SelectionManager.selections[i]; - var initialProperties = SelectionManager.savedProperties[entityID.id]; - Entities.editEntity(entityID, initialProperties); - } - } else { - pushCommandForSelections(); - } + Overlays.editOverlay(rotateOverlayInner, { visible: false }); + Overlays.editOverlay(rotateOverlayOuter, { visible: false }); + Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + + pushCommandForSelections(); }, onMove: function(event) { - if (!entitySelected || mode !== "ROTATE_ROLL") { - return; // not allowed - } var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); if (debug) { @@ -1804,9 +1844,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (debug) { diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index aa38baf9e2..a34f3d3985 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -1,4 +1,4 @@ -// + // newEditEntities.js // examples // @@ -73,6 +73,7 @@ var toolBar = (function () { newModelButton, newCubeButton, newSphereButton, + newLightButton, browseModelsButton, loadURLMenuItem, loadFileMenuItem, @@ -157,6 +158,15 @@ var toolBar = (function () { visible: true }); + newLightButton = toolBar.addTool({ + imageURL: toolIconUrl + "light.svg", + 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) { @@ -272,7 +282,7 @@ var toolBar = (function () { toggleNewModelButton(false); file = Window.browse("Select your model file ...", - Settings.getValue("LastModelUploadLocation").path(), + Settings.getValue("LastModelUploadLocation").path(), "Model files (*.fst *.fbx)"); //"Model files (*.fst *.fbx *.svo)"); if (file !== null) { @@ -295,7 +305,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: "Box", position: position, dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION }, @@ -312,14 +322,39 @@ 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: "Sphere", position: position, dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION }, color: { red: 255, green: 0, blue: 0 } }); } else { - print("Can't create box: Box would be out of bounds."); + print("Can't create sphere: Sphere would be out of bounds."); + } + return true; + } + + if (newLightButton === 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: "Light", + position: position, + dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION }, + isSpotlight: false, + diffuseColor: { red: 255, green: 255, blue: 255 }, + ambientColor: { red: 255, green: 255, blue: 255 }, + specularColor: { red: 0, green: 0, blue: 0 }, + + constantAttenuation: 1, + linearAttenuation: 0, + quadraticAttenuation: 0, + exponent: 0, + cutoff: 180, // in degrees + }); + } else { + print("Can't create Light: Light would be out of bounds."); } return true; } @@ -433,7 +468,7 @@ function mousePressEvent(event) { var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); if (0 < x && sizeOK) { @@ -467,7 +502,7 @@ function mousePressEvent(event) { w: selectedEntityProperties.rotation.w, }; selectedEntityProperties.glowLevel = 0.0; - + print("Clicked on " + selectedEntityID.id + " " + entitySelected); tooltip.updateText(selectedEntityProperties); tooltip.show(true); @@ -481,7 +516,7 @@ function mouseMoveEvent(event) { if (!isActive) { return; } - + // allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing if (selectionDisplay.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) { return; @@ -496,11 +531,11 @@ function mouseMoveEvent(event) { } var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0; - - var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), + + var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), entityIntersection.properties.position)) * 180 / 3.14; - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); if (entityIntersection.entityID.isKnownID && sizeOK) { @@ -510,7 +545,7 @@ function mouseMoveEvent(event) { highlightedEntityID = entityIntersection.entityID; selectionDisplay.highlightSelectable(entityIntersection.entityID); } - + } } @@ -550,9 +585,9 @@ function setupModelMenus() { } Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L", + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L", afterItem: "Paste Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S", + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S", afterItem: "Allow Select Large Models", isCheckable: true }); Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); @@ -606,14 +641,24 @@ function handeMenuEvent(menuItem) { } else if (menuItem == "Allow Select Large Models") { allowLargeModels = Menu.isOptionChecked("Allow Select Large Models"); } else if (menuItem == "Delete") { - if (entitySelected) { + if (SelectionManager.hasSelection()) { print(" Delete Entity.... selectedEntityID="+ selectedEntityID); + SelectionManager.saveProperties(); + var savedProperties = []; for (var i = 0; i < selectionManager.selections.length; i++) { - Entities.deleteEntity(selectionManager.selections[i]); + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID.id]; + SelectionManager.savedProperties[entityID.id]; + savedProperties.push({ + entityID: entityID, + properties: initialProperties + }); + Entities.deleteEntity(entityID); } + SelectionManager.clearSelections(); + pushCommandForSelections([], savedProperties); selectionDisplay.unselect(selectedEntityID); entitySelected = false; - selectionManager.clearSelections(); } else { print(" Delete Entity.... not holding..."); } @@ -623,15 +668,16 @@ function handeMenuEvent(menuItem) { editModelID = -1; if (selectionManager.selections.length == 1) { print(" Edit Properties.... selectedEntityID="+ selectedEntityID); - editModelID = selectedEntityID; + editModelID = selectionManager.selections[0]; } else { print(" Edit Properties.... not holding..."); } if (editModelID != -1) { print(" Edit Properties.... about to edit properties..."); entityPropertyDialogBox.openDialog(editModelID); + selectionManager._update(); } - + } else if (menuItem == "Paste Models") { modelImporter.paste(); } else if (menuItem == "Export Models") { @@ -659,8 +705,6 @@ Controller.keyReleaseEvent.connect(function (event) { handeMenuEvent("Delete"); } else if (event.text == "TAB") { selectionDisplay.toggleSpaceMode(); - } else if (event.text == "ESC") { - selectionDisplay.cancelTool(); } else if (event.text == "f") { if (entitySelected) { // Get latest properties @@ -671,8 +715,130 @@ Controller.keyReleaseEvent.connect(function (event) { if (isActive) { cameraManager.enable(); } + } else { + var delta = null; + + if (event.text == 'UP') { + if (event.isControl || event.isAlt) { + delta = { x: 0, y: 1, z: 0 }; + } else { + delta = { x: 0, y: 0, z: -1 }; + } + } else if (event.text == 'DOWN') { + if (event.isControl || event.isAlt) { + delta = { x: 0, y: -1, z: 0 }; + } else { + delta = { x: 0, y: 0, z: 1 }; + } + } else if (event.text == 'LEFT') { + delta = { x: -1, y: 0, z: 0 }; + } else if (event.text == 'RIGHT') { + delta = { x: 1, y: 0, z: 0 }; + } + + if (delta != null) { + // Adjust delta so that movements are relative to the current camera orientation + var lookDirection = Quat.getFront(Camera.getOrientation()); + lookDirection.z *= -1; + + var angle = Math.atan2(lookDirection.z, lookDirection.x); + angle -= (Math.PI / 4); + + var rotation = Math.floor(angle / (Math.PI / 2)) * (Math.PI / 2); + var rotator = Quat.fromPitchYawRollRadians(0, rotation, 0); + + delta = Vec3.multiplyQbyV(rotator, delta); + + SelectionManager.saveProperties(); + + for (var i = 0; i < selectionManager.selections.length; i++) { + var entityID = selectionManager.selections[i]; + var properties = Entities.getEntityProperties(entityID); + Entities.editEntity(entityID, { + position: Vec3.sum(properties.position, delta) + }); + } + + pushCommandForSelections(); + + selectionManager._update(); + } } }); +// When an entity has been deleted we need a way to "undo" this deletion. Because it's not currently +// possible to create an entity with a specific id, earlier undo commands to the deleted entity +// will fail if there isn't a way to find the new entity id. +DELETED_ENTITY_MAP = { +} +function applyEntityProperties(data) { + var properties = data.setProperties; + var selectedEntityIDs = []; + for (var i = 0; i < properties.length; i++) { + var entityID = properties[i].entityID; + if (DELETED_ENTITY_MAP[entityID.id] !== undefined) { + entityID = DELETED_ENTITY_MAP[entityID.id]; + } + Entities.editEntity(entityID, properties[i].properties); + selectedEntityIDs.push(entityID); + } + for (var i = 0; i < data.createEntities.length; i++) { + var entityID = data.createEntities[i].entityID; + var properties = data.createEntities[i].properties; + var newEntityID = Entities.addEntity(properties); + DELETED_ENTITY_MAP[entityID.id] = newEntityID; + print(newEntityID.isKnownID); + if (data.selectCreated) { + selectedEntityIDs.push(newEntityID); + } + } + for (var i = 0; i < data.deleteEntities.length; i++) { + var entityID = data.deleteEntities[i].entityID; + if (DELETED_ENTITY_MAP[entityID.id] !== undefined) { + entityID = DELETED_ENTITY_MAP[entityID.id]; + } + Entities.deleteEntity(entityID); + } + selectionManager.setSelections(selectedEntityIDs); +}; + +// For currently selected entities, push a command to the UndoStack that uses the current entity properties for the +// redo command, and the saved properties for the undo command. Also, include create and delete entity data. +function pushCommandForSelections(createdEntityData, deletedEntityData) { + var undoData = { + setProperties: [], + createEntities: deletedEntityData || [], + deleteEntities: createdEntityData || [], + selectCreated: true, + }; + var redoData = { + setProperties: [], + createEntities: createdEntityData || [], + deleteEntities: deletedEntityData || [], + selectCreated: false, + }; + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID.id]; + var currentProperties = Entities.getEntityProperties(entityID); + undoData.setProperties.push({ + entityID: entityID, + properties: { + position: initialProperties.position, + rotation: initialProperties.rotation, + dimensions: initialProperties.dimensions, + }, + }); + redoData.setProperties.push({ + entityID: entityID, + properties: { + position: currentProperties.position, + rotation: currentProperties.rotation, + dimensions: currentProperties.dimensions, + }, + }); + } + UndoStack.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData); +} diff --git a/examples/spotlightExample.js b/examples/spotlightExample.js new file mode 100644 index 0000000000..5eb5432f3f --- /dev/null +++ b/examples/spotlightExample.js @@ -0,0 +1,37 @@ +// +// spotlightExample.js +// examples +// +// Created by Brad Hefta-Gaub on 10/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example script that demonstrates creating and editing a particle +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var lightProperties = { + type: "Light", + position: { x: 0, y: 0, z: 0 }, + dimensions: { x: 1000, y: 1000, z: 1000 }, + angularVelocity: { x: 0, y: 10, z: 0 }, + angularDamping: 0, + + isSpotlight: true, + diffuseColor: { red: 255, green: 255, blue: 255 }, + ambientColor: { red: 255, green: 255, blue: 255 }, + specularColor: { red: 255, green: 255, blue: 255 }, + + constantAttenuation: 1, + linearAttenuation: 0, + quadraticAttenuation: 0, + exponent: 0, + cutoff: 180, // in degrees +}; + +var spotlightID = Entities.addEntity(lightProperties); + +Script.scriptEnding.connect(function() { + Entities.deleteEntity(spotlightID); +}); diff --git a/ice-server/src/main.cpp b/ice-server/src/main.cpp index 21c8b563b1..2326984668 100644 --- a/ice-server/src/main.cpp +++ b/ice-server/src/main.cpp @@ -11,7 +11,7 @@ #include -#include +#include #include "IceServer.h" @@ -20,7 +20,7 @@ int main(int argc, char* argv[]) { setvbuf(stdout, NULL, _IOLBF, 0); #endif - qInstallMessageHandler(Logging::verboseMessageHandler); + qInstallMessageHandler(LogHandler::verboseMessageHandler); IceServer iceServer(argc, argv); return iceServer.exec(); diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index cdd9337210..64093e91ac 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -101,7 +101,7 @@ endif() add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM}) # link required hifi libraries -link_hifi_libraries(shared octree voxels fbx metavoxels networking entities avatars audio animation script-engine) +link_hifi_libraries(shared octree voxels fbx metavoxels networking entities avatars audio animation script-engine physics) # find any optional and required libraries find_package(ZLIB REQUIRED) diff --git a/interface/external/gverb/readme.txt b/interface/external/gverb/readme.txt index aa2fe8a602..1a85659b91 100644 --- a/interface/external/gverb/readme.txt +++ b/interface/external/gverb/readme.txt @@ -1,7 +1,7 @@ Instructions for adding the Gverb library to Interface (This is a required library) -Clément Brisset, Octobre 22nd, 2014 +Clément Brisset, October 22nd, 2014 1. Go to https://github.com/highfidelity/gverb Or download the sources directly via this link: diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1d26d79335..0985e54845 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -58,7 +58,7 @@ #include #include #include -#include +#include #include #include #include @@ -117,12 +117,10 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D const QString DEFAULT_SCRIPTS_JS_URL = "http://public.highfidelity.io/scripts/defaultScripts.js"; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - if (message.size() > 0) { - QString dateString = QDateTime::currentDateTime().toTimeSpec(Qt::LocalTime).toString(Qt::ISODate); - QString formattedMessage = QString("[%1] %2\n").arg(dateString).arg(message); - - fprintf(stdout, "%s", qPrintable(formattedMessage)); - Application::getInstance()->getLogger()->addMessage(qPrintable(formattedMessage)); + QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); + + if (!logMessage.isEmpty()) { + Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage)); } } @@ -582,10 +580,6 @@ void Application::initializeGL() { float startupTime = (float)_applicationStartupTime.elapsed() / 1000.0; _justStarted = false; qDebug("Startup time: %4.2f seconds.", startupTime); - const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time"; - - // ask the Logstash class to record the startup time - Logging::stashValue(STAT_TYPE_TIMER, LOGSTASH_INTERFACE_START_TIME_KEY, startupTime); } // update before the first render @@ -3179,7 +3173,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { } else { // if not rendering the billboard, the region is in device independent coordinates; must convert to device QSize size = getTextureCache()->getFrameBufferSize(); - float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); + float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * _renderResolutionScale; int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; glViewport(x, size.height() - y - height, width, height); glScissor(x, size.height() - y - height, width, height); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index dd84eb3211..7d039387bb 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -1018,6 +1018,27 @@ void Audio::parseAudioStreamStatsPacket(const QByteArray& packet) { } } +void Audio::parseAudioEnvironmentData(const QByteArray &packet) { + int numBytesPacketHeader = numBytesForPacketHeader(packet); + const char* dataAt = packet.constData() + numBytesPacketHeader; + + char bitset; + memcpy(&bitset, dataAt, sizeof(char)); + dataAt += sizeof(char); + + bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT);; + if (hasReverb) { + float reverbTime, wetLevel; + memcpy(&reverbTime, dataAt, sizeof(float)); + dataAt += sizeof(float); + memcpy(&wetLevel, dataAt, sizeof(float)); + dataAt += sizeof(float); + _receivedAudioStream.setReverb(reverbTime, wetLevel); + } else { + _receivedAudioStream.clearReverb(); + } +} + void Audio::sendDownstreamAudioStatsPacket() { // since this function is called every second, we'll sample for some of our stats here diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 900b6ce0d6..fcbfb12761 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -125,6 +125,7 @@ public slots: void stop(); void addReceivedAudioToStream(const QByteArray& audioByteArray); void parseAudioStreamStatsPacket(const QByteArray& packet); + void parseAudioEnvironmentData(const QByteArray& packet); void addSpatialAudioToBuffer(unsigned int sampleTime, const QByteArray& spatialAudio, unsigned int numSamples); void handleAudioInput(); void reset(); diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 868654b9da..6f9f4cae68 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -49,14 +49,18 @@ void DatagramProcessor::processDatagrams() { PacketType incomingType = packetTypeForPacket(incomingPacket); // only process this packet if we have a match on the packet version switch (incomingType) { + case PacketTypeAudioEnvironment: + case PacketTypeAudioStreamStats: case PacketTypeMixedAudio: - case PacketTypeSilentAudioFrame: - case PacketTypeAudioStreamStats: { - if (incomingType != PacketTypeAudioStreamStats) { - QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToStream", Qt::QueuedConnection, + case PacketTypeSilentAudioFrame: { + if (incomingType == PacketTypeAudioStreamStats) { + QMetaObject::invokeMethod(&application->_audio, "parseAudioStreamStatsPacket", Qt::QueuedConnection, + Q_ARG(QByteArray, incomingPacket)); + } else if (incomingType == PacketTypeAudioEnvironment) { + QMetaObject::invokeMethod(&application->_audio, "parseAudioEnvironmentData", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else { - QMetaObject::invokeMethod(&application->_audio, "parseAudioStreamStatsPacket", Qt::QueuedConnection, + QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToStream", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3fb3eb569a..4e453bfde2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1314,7 +1314,7 @@ void Menu::displayNameLocationResponse(const QString& errorString) { void Menu::toggleLocationList() { if (!_userLocationsDialog) { JavascriptObjectMap locationObjectMap; - locationObjectMap.insert("InterfaceLocation", LocationScriptingInterface::getInstance()); + locationObjectMap.insert("InterfaceLocation", &AddressManager::getInstance()); _userLocationsDialog = DataWebDialog::dialogForPath("/user/locations", locationObjectMap); } @@ -1358,7 +1358,7 @@ void Menu::nameLocation() { if (!_newLocationDialog) { JavascriptObjectMap locationObjectMap; - locationObjectMap.insert("InterfaceLocation", LocationScriptingInterface::getInstance()); + locationObjectMap.insert("InterfaceLocation", &AddressManager::getInstance()); _newLocationDialog = DataWebDialog::dialogForPath("/user/locations/new", locationObjectMap); } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index d0f1d75ed6..e760813fe5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -655,19 +655,19 @@ void SkeletonModel::buildShapes() { Shape::Type type = joint.shapeType; int parentIndex = joint.parentIndex; if (parentIndex == -1 || radius < EPSILON) { - type = UNKNOWN_SHAPE; - } else if (type == CAPSULE_SHAPE && halfHeight < EPSILON) { + type = SHAPE_TYPE_UNKNOWN; + } else if (type == SHAPE_TYPE_CAPSULE && halfHeight < EPSILON) { // this shape is forced to be a sphere - type = SPHERE_SHAPE; + type = SHAPE_TYPE_SPHERE; } Shape* shape = NULL; - if (type == SPHERE_SHAPE) { + if (type == SHAPE_TYPE_SPHERE) { shape = new VerletSphereShape(radius, &(points[i])); shape->setEntity(this); float mass = massScale * glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume()); points[i].setMass(mass); totalMass += mass; - } else if (type == CAPSULE_SHAPE) { + } else if (type == SHAPE_TYPE_CAPSULE) { assert(parentIndex != -1); shape = new VerletCapsuleShape(radius, &(points[parentIndex]), &(points[i])); shape->setEntity(this); diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 2c7c970376..7eb28c2d3e 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -24,6 +24,7 @@ #include "EntityTreeRenderer.h" #include "RenderableBoxEntityItem.h" +#include "RenderableLightEntityItem.h" #include "RenderableModelEntityItem.h" #include "RenderableSphereEntityItem.h" @@ -39,6 +40,7 @@ EntityTreeRenderer::EntityTreeRenderer() : 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) } EntityTreeRenderer::~EntityTreeRenderer() { diff --git a/interface/src/entities/RenderableLightEntityItem.cpp b/interface/src/entities/RenderableLightEntityItem.cpp new file mode 100644 index 0000000000..bf9939e164 --- /dev/null +++ b/interface/src/entities/RenderableLightEntityItem.cpp @@ -0,0 +1,89 @@ +// +// RenderableLightEntityItem.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 + +#include "InterfaceConfig.h" + +#include +#include + + +#include "Application.h" +#include "Menu.h" +#include "EntityTreeRenderer.h" +#include "RenderableLightEntityItem.h" + + +EntityItem* RenderableLightEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + return new RenderableLightEntityItem(entityID, properties); +} + +void RenderableLightEntityItem::render(RenderArgs* args) { + PerformanceTimer perfTimer("RenderableLightEntityItem::render"); + assert(getType() == EntityTypes::Light); + glm::vec3 position = getPositionInMeters(); + glm::vec3 center = getCenterInMeters(); + glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE; + glm::quat rotation = getRotation(); + float largestDiameter = glm::max(dimensions.x, dimensions.y, dimensions.z); + + const float MAX_COLOR = 255.0f; + float diffuseR = getDiffuseColor()[RED_INDEX] / MAX_COLOR; + float diffuseG = getDiffuseColor()[GREEN_INDEX] / MAX_COLOR; + float diffuseB = getDiffuseColor()[BLUE_INDEX] / MAX_COLOR; + + float ambientR = getAmbientColor()[RED_INDEX] / MAX_COLOR; + float ambientG = getAmbientColor()[GREEN_INDEX] / MAX_COLOR; + float ambientB = getAmbientColor()[BLUE_INDEX] / MAX_COLOR; + + float specularR = getSpecularColor()[RED_INDEX] / MAX_COLOR; + float specularG = getSpecularColor()[GREEN_INDEX] / MAX_COLOR; + float specularB = getSpecularColor()[BLUE_INDEX] / MAX_COLOR; + + glm::vec3 ambient = glm::vec3(ambientR, ambientG, ambientB); + glm::vec3 diffuse = glm::vec3(diffuseR, diffuseG, diffuseB); + glm::vec3 specular = glm::vec3(specularR, specularG, specularB); + glm::vec3 direction = IDENTITY_FRONT * rotation; + float constantAttenuation = getConstantAttenuation(); + float linearAttenuation = getLinearAttenuation(); + float quadraticAttenuation = getQuadraticAttenuation(); + float exponent = getExponent(); + float cutoff = glm::radians(getCutoff()); + + if (_isSpotlight) { + Application::getInstance()->getDeferredLightingEffect()->addSpotLight(position, largestDiameter / 2.0f, + ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation, + direction, exponent, cutoff); + } else { + Application::getInstance()->getDeferredLightingEffect()->addPointLight(position, largestDiameter / 2.0f, + ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation); + } + + bool wantDebug = false; + if (wantDebug) { + glColor4f(diffuseR, diffuseG, diffuseB, 1.0f); + 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); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + + glScalef(dimensions.x, dimensions.y, dimensions.z); + Application::getInstance()->getDeferredLightingEffect()->renderWireSphere(0.5f, 15, 15); + glPopMatrix(); + glPopMatrix(); + } +}; diff --git a/interface/src/entities/RenderableLightEntityItem.h b/interface/src/entities/RenderableLightEntityItem.h new file mode 100644 index 0000000000..cecd9b761e --- /dev/null +++ b/interface/src/entities/RenderableLightEntityItem.h @@ -0,0 +1,40 @@ +// +// RenderableLightEntityItem.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_RenderableLightEntityItem_h +#define hifi_RenderableLightEntityItem_h + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +class RenderableLightEntityItem : public LightEntityItem { +public: + static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + RenderableLightEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + LightEntityItem(entityItemID, properties) + { } + + virtual void render(RenderArgs* args); +}; + + +#endif // hifi_RenderableLightEntityItem_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 1c1cfd807e..a872cf2c33 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1738,7 +1738,8 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl mesh.texCoords.size() * sizeof(glm::vec2) + (mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0); //skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4); - GLBATCH(glVertexAttribPointer)(skinLocations->clusterIndices, 4, GL_FLOAT, GL_TRUE, 0, (const void*) offset); + GLBATCH(glVertexAttribPointer)(skinLocations->clusterIndices, 4, GL_FLOAT, GL_TRUE, 0, + reinterpret_cast(offset)); //skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT, // offset + vertexCount * sizeof(glm::vec4), 4); GLBATCH(glVertexAttribPointer)(skinLocations->clusterWeights, 4, GL_FLOAT, GL_TRUE, 0, (const void*) (offset + vertexCount * sizeof(glm::vec4))); diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 59578951f8..366659b633 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -83,6 +83,12 @@ void InboundAudioStream::clearBuffer() { _currentJitterBufferFrames = 0; } +void InboundAudioStream::setReverb(float reverbTime, float wetLevel) { + _hasReverb = true; + _reverbTime = reverbTime; + _wetLevel = wetLevel; +} + void InboundAudioStream::perSecondCallbackForUpdatingStats() { _incomingSequenceNumberStats.pushStatsToHistory(); _timeGapStatsForDesiredCalcOnTooManyStarves.currentIntervalComplete(); @@ -163,22 +169,9 @@ int InboundAudioStream::parseData(const QByteArray& packet) { } int InboundAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) { - int read = 0; - if (type == PacketTypeMixedAudio) { - memcpy(&_hasReverb, packetAfterSeqNum.data() + read, sizeof(bool)); - read += sizeof(bool); - - if (_hasReverb) { - memcpy(&_reverbTime, packetAfterSeqNum.data() + read, sizeof(float)); - read += sizeof(float); - memcpy(&_wetLevel, packetAfterSeqNum.data() + read, sizeof(float)); - read += sizeof(float); - } - } - // mixed audio packets do not have any info between the seq num and the audio data. - numAudioSamples = (packetAfterSeqNum.size() - read) / sizeof(int16_t); - return read; + numAudioSamples = packetAfterSeqNum.size() / sizeof(int16_t); + return 0; } int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int numAudioSamples) { diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index 3e69db0afb..ad2b7266ed 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -45,6 +45,9 @@ const int DEFAULT_WINDOW_SECONDS_FOR_DESIRED_CALC_ON_TOO_MANY_STARVES = 50; const int DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION = 10; const bool DEFAULT_REPETITION_WITH_FADE = true; +// Audio Env bitset +const int HAS_REVERB_BIT = 0; // 1st bit + class InboundAudioStream : public NodeData { Q_OBJECT public: @@ -158,6 +161,8 @@ public: bool hasReverb() const { return _hasReverb; } float getRevebTime() const { return _reverbTime; } float getWetLevel() const { return _wetLevel; } + void setReverb(float reverbTime, float wetLevel); + void clearReverb() { _hasReverb = false; } public slots: /// This function should be called every second for all the stats to function properly. If dynamic jitter buffers diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index 1d287ee7a2..42b3cf7d3c 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -5,8 +5,8 @@ setup_hifi_library(Network Script) include_glm() -link_hifi_libraries(shared octree voxels networking) +link_hifi_libraries(shared octree voxels networking physics) include_hifi_library_headers(fbx) # call macro to link our dependencies and bubble them up via a property on our target -link_shared_dependencies() \ No newline at end of file +link_shared_dependencies() diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 40d71a032d..e48baa7615 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -5,7 +5,7 @@ setup_hifi_library(Network Script) include_glm() -link_hifi_libraries(shared octree fbx networking animation) +link_hifi_libraries(shared octree fbx networking animation physics) # call macro to link our dependencies and bubble them up via a property on our target link_shared_dependencies() diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index b533db3015..d41bd4a179 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -18,6 +18,7 @@ #include #include // for Animation, AnimationCache, and AnimationPointer classes +#include #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState #include diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 90f56e36b9..83d13e369d 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -68,8 +68,8 @@ EntityItemProperties::EntityItemProperties() : _animationFPS(ModelEntityItem::DEFAULT_ANIMATION_FPS), _glowLevel(0.0f), _localRenderAlpha(1.0f), + _isSpotlight(false), - _naturalDimensions(1.0f, 1.0f, 1.0f), _colorChanged(false), _modelURLChanged(false), _animationURLChanged(false), @@ -78,11 +78,51 @@ EntityItemProperties::EntityItemProperties() : _animationFPSChanged(false), _glowLevelChanged(false), _localRenderAlphaChanged(false), + _isSpotlightChanged(false), - _defaultSettings(true) + _diffuseColor(), + _ambientColor(), + _specularColor(), + _constantAttenuation(1.0f), + _linearAttenuation(0.0f), + _quadraticAttenuation(0.0f), + _exponent(0.0f), + _cutoff(PI), + + _diffuseColorChanged(false), + _ambientColorChanged(false), + _specularColorChanged(false), + _constantAttenuationChanged(false), + _linearAttenuationChanged(false), + _quadraticAttenuationChanged(false), + _exponentChanged(false), + _cutoffChanged(false), + + _defaultSettings(true), + _sittingPoints(NULL), + _naturalDimensions(1.0f, 1.0f, 1.0f) { } +EntityItemProperties::~EntityItemProperties() { + if (_sittingPoints) { + delete _sittingPoints; + _sittingPoints = NULL; + } +} + +void EntityItemProperties::setSittingPoints(const QVector& sittingPoints) { + if (!_sittingPoints) { + _sittingPoints = new QVector; + } + _sittingPoints->clear(); + + foreach (SittingPoint sitPoint, sittingPoints) { + _sittingPoints->append(sitPoint); + } +} + + void EntityItemProperties::debugDump() const { qDebug() << "EntityItemProperties..."; qDebug() << " _type=" << EntityTypes::getEntityTypeName(_type); @@ -120,6 +160,15 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping); CHECK_PROPERTY_CHANGE(PROP_IGNORE_FOR_COLLISIONS, ignoreForCollisions); CHECK_PROPERTY_CHANGE(PROP_COLLISIONS_WILL_MOVE, collisionsWillMove); + CHECK_PROPERTY_CHANGE(PROP_IS_SPOTLIGHT, isSpotlight); + CHECK_PROPERTY_CHANGE(PROP_DIFFUSE_COLOR, diffuseColor); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_COLOR, ambientColor); + CHECK_PROPERTY_CHANGE(PROP_SPECULAR_COLOR, specularColor); + CHECK_PROPERTY_CHANGE(PROP_CONSTANT_ATTENUATION, constantAttenuation); + CHECK_PROPERTY_CHANGE(PROP_LINEAR_ATTENUATION, linearAttenuation); + CHECK_PROPERTY_CHANGE(PROP_QUADRATIC_ATTENUATION, quadraticAttenuation); + CHECK_PROPERTY_CHANGE(PROP_EXPONENT, exponent); + CHECK_PROPERTY_CHANGE(PROP_CUTOFF, cutoff); return changedProperties; } @@ -161,17 +210,30 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha); COPY_PROPERTY_TO_QSCRIPTVALUE(ignoreForCollisions); COPY_PROPERTY_TO_QSCRIPTVALUE(collisionsWillMove); + COPY_PROPERTY_TO_QSCRIPTVALUE(isSpotlight); + COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(diffuseColor, getDiffuseColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(ambientColor, getAmbientColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(specularColor, getSpecularColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE(constantAttenuation); + COPY_PROPERTY_TO_QSCRIPTVALUE(linearAttenuation); + COPY_PROPERTY_TO_QSCRIPTVALUE(quadraticAttenuation); + COPY_PROPERTY_TO_QSCRIPTVALUE(exponent); + COPY_PROPERTY_TO_QSCRIPTVALUE(cutoff); // Sitting properties support QScriptValue sittingPoints = engine->newObject(); - for (int i = 0; i < _sittingPoints.size(); ++i) { - QScriptValue sittingPoint = engine->newObject(); - sittingPoint.setProperty("name", _sittingPoints[i].name); - sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints[i].position)); - sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints[i].rotation)); - sittingPoints.setProperty(i, sittingPoint); + if (_sittingPoints) { + for (int i = 0; i < _sittingPoints->size(); ++i) { + QScriptValue sittingPoint = engine->newObject(); + sittingPoint.setProperty("name", _sittingPoints->at(i).name); + sittingPoint.setProperty("position", vec3toScriptValue(engine, _sittingPoints->at(i).position)); + sittingPoint.setProperty("rotation", quatToScriptValue(engine, _sittingPoints->at(i).rotation)); + sittingPoints.setProperty(i, sittingPoint); + } + sittingPoints.setProperty("length", _sittingPoints->size()); + } else { + sittingPoints.setProperty("length", 0); } - sittingPoints.setProperty("length", _sittingPoints.size()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable AABox aaBox = getAABoxInMeters(); @@ -220,6 +282,15 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(localRenderAlpha, setLocalRenderAlpha); COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(ignoreForCollisions, setIgnoreForCollisions); COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(collisionsWillMove, setCollisionsWillMove); + COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(isSpotlight, setIsSpotlight); + COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(diffuseColor, setDiffuseColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(ambientColor, setAmbientColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(specularColor, setSpecularColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(constantAttenuation, setConstantAttenuation); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(linearAttenuation, setLinearAttenuation); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(quadraticAttenuation, setQuadraticAttenuation); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(exponent, setExponent); + COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(cutoff, setCutoff); _lastEdited = usecTimestampNow(); } @@ -370,6 +441,15 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem 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()); } if (propertyCount > 0) { int endOfEntityItemData = packetData->getUncompressedByteOffset(); @@ -568,6 +648,15 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int 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); return valid; } @@ -622,6 +711,16 @@ void EntityItemProperties::markAllChanged() { _animationFPSChanged = true; _glowLevelChanged = true; _localRenderAlphaChanged = true; + _isSpotlightChanged = true; + + _diffuseColorChanged = true; + _ambientColorChanged = true; + _specularColorChanged = true; + _constantAttenuationChanged = true; + _linearAttenuationChanged = true; + _quadraticAttenuationChanged = true; + _exponentChanged = true; + _cutoffChanged = true; } AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6e1594fb9b..f977b346cc 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -32,7 +32,6 @@ #include "EntityItemPropertiesMacros.h" #include "EntityTypes.h" -// PropertyFlags support enum EntityPropertyList { PROP_PAGED_PROPERTY, PROP_CUSTOM_PROPERTIES_INCLUDED, @@ -65,7 +64,18 @@ enum EntityPropertyList { PROP_IGNORE_FOR_COLLISIONS, PROP_COLLISIONS_WILL_MOVE, - PROP_LAST_ITEM = PROP_COLLISIONS_WILL_MOVE + // property used by Light entity + PROP_IS_SPOTLIGHT, + PROP_DIFFUSE_COLOR, + PROP_AMBIENT_COLOR, + PROP_SPECULAR_COLOR, + PROP_CONSTANT_ATTENUATION, + PROP_LINEAR_ATTENUATION, + PROP_QUADRATIC_ATTENUATION, + PROP_EXPONENT, + PROP_CUTOFF, + + PROP_LAST_ITEM = PROP_CUTOFF }; typedef PropertyFlags EntityPropertyFlags; @@ -83,9 +93,10 @@ class EntityItemProperties { friend class ModelEntityItem; // TODO: consider removing this friend relationship and use public methods 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 public: EntityItemProperties(); - virtual ~EntityItemProperties() { }; + virtual ~EntityItemProperties(); virtual QScriptValue copyToScriptValue(QScriptEngine* engine) const; virtual void copyFromScriptValue(const QScriptValue& object); @@ -209,8 +220,7 @@ public: void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; } void markAllChanged(); - QVector getSittingPoints() const { return _sittingPoints; } - void setSittingPoints(QVector sittingPoints) { _sittingPoints = sittingPoints; } + void setSittingPoints(const QVector& sittingPoints); const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; } void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; } @@ -233,6 +243,36 @@ public: 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; } + + bool getConstantAttenuation() const { return _constantAttenuation; } + void setConstantAttenuation(float value) { _constantAttenuation = value; _constantAttenuationChanged = true; } + + bool getLinearAttenuation() const { return _linearAttenuation; } + void setLinearAttenuation(float value) { _linearAttenuation = value; _linearAttenuationChanged = true; } + + bool getQuadraticAttenuation() const { return _quadraticAttenuation; } + void setQuadraticAttenuation(float value) { _quadraticAttenuation = value; _quadraticAttenuationChanged = true; } + + bool getExponent() const { return _exponent; } + void setExponent(bool value) { _exponent = value; _exponentChanged = true; } + + bool getCutoff() const { return _cutoff; } + void setCutoff(bool value) { _cutoff = value; _cutoffChanged = true; } + void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; } private: @@ -288,8 +328,7 @@ private: float _animationFPS; float _glowLevel; float _localRenderAlpha; - QVector _sittingPoints; - glm::vec3 _naturalDimensions; + bool _isSpotlight; bool _colorChanged; bool _modelURLChanged; @@ -299,8 +338,32 @@ private: bool _animationFPSChanged; bool _glowLevelChanged; bool _localRenderAlphaChanged; + bool _isSpotlightChanged; + + xColor _diffuseColor; + xColor _ambientColor; + xColor _specularColor; + float _constantAttenuation; + float _linearAttenuation; + float _quadraticAttenuation; + float _exponent; + float _cutoff; + + bool _diffuseColorChanged; + bool _ambientColorChanged; + bool _specularColorChanged; + bool _constantAttenuationChanged; + bool _linearAttenuationChanged; + bool _quadraticAttenuationChanged; + bool _exponentChanged; + bool _cutoffChanged; bool _defaultSettings; + + // NOTE: The following are pseudo client only properties. They are only used in clients which can access + // properties of model geometry. But these properties are not serialized like other properties. + QVector* _sittingPoints; + glm::vec3 _naturalDimensions; }; Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 16f883a36a..b5a489f88c 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -147,6 +147,10 @@ QScriptValue P = xColorToScriptValue(engine, _##P); \ properties.setProperty(#P, P); +#define COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(P,G) \ + QScriptValue P = xColorToScriptValue(engine, G); \ + properties.setProperty(#P, P); + #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(P, G) \ properties.setProperty(#P, G); diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 7505c3f768..aaa297f4fd 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -19,6 +19,7 @@ #include "EntityTypes.h" #include "BoxEntityItem.h" +#include "LightEntityItem.h" #include "ModelEntityItem.h" #include "SphereEntityItem.h" @@ -33,6 +34,7 @@ const QString ENTITY_TYPE_NAME_UNKNOWN = "Unknown"; REGISTER_ENTITY_TYPE(Model) REGISTER_ENTITY_TYPE(Box) REGISTER_ENTITY_TYPE(Sphere) +REGISTER_ENTITY_TYPE(Light) const QString& EntityTypes::getEntityTypeName(EntityType entityType) { diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 8851b04b7e..85bbff99ef 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -33,7 +33,8 @@ public: Model, Box, Sphere, - LAST = Sphere + Light, + LAST = Light } EntityType; static const QString& getEntityTypeName(EntityType entityType); diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp new file mode 100644 index 0000000000..20f28cd98c --- /dev/null +++ b/libraries/entities/src/LightEntityItem.cpp @@ -0,0 +1,148 @@ +// +// LightEntityItem.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 "LightEntityItem.h" + + +EntityItem* LightEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + return new LightEntityItem(entityID, properties); +} + +// our non-pure virtual subclass for now... +LightEntityItem::LightEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + EntityItem(entityItemID, properties) +{ + _type = EntityTypes::Light; + + // default property values + const quint8 MAX_COLOR = 255; + _ambientColor[RED_INDEX] = _ambientColor[GREEN_INDEX] = _ambientColor[BLUE_INDEX] = 0; + _diffuseColor[RED_INDEX] = _diffuseColor[GREEN_INDEX] = _diffuseColor[BLUE_INDEX] = MAX_COLOR; + _specularColor[RED_INDEX] = _specularColor[GREEN_INDEX] = _specularColor[BLUE_INDEX] = MAX_COLOR; + _constantAttenuation = 1.0f; + _linearAttenuation = 0.0f; + _quadraticAttenuation = 0.0f; + _exponent = 0.0f; + _cutoff = PI; + + setProperties(properties, true); + + // a light is not collide-able so we make it's shape be a tiny sphere at origin + _emptyShape.setTranslation(glm::vec3(0.0f, 0.0f, 0.0f)); + _emptyShape.setRadius(0.0f); +} + +EntityItemProperties LightEntityItem::getProperties() const { + EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(isSpotlight, getIsSpotlight); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(diffuseColor, getDiffuseXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientColor, getAmbientXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(specularColor, getSpecularXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(constantAttenuation, getConstantAttenuation); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(linearAttenuation, getLinearAttenuation); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(quadraticAttenuation, getQuadraticAttenuation); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(exponent, getExponent); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cutoff, getCutoff); + + return properties; +} + +bool LightEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) { + bool somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(isSpotlight, setIsSpotlight); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(diffuseColor, setDiffuseColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientColor, setAmbientColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(specularColor, setSpecularColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(isSpotlight, setIsSpotlight); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(constantAttenuation, setConstantAttenuation); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(linearAttenuation, setLinearAttenuation); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(quadraticAttenuation, setQuadraticAttenuation); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(exponent, setExponent); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cutoff, setCutoff); + + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qDebug() << "LightEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties.getLastEdited()); + } + return somethingChanged; +} + +int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, _isSpotlight); + READ_ENTITY_PROPERTY_COLOR(PROP_DIFFUSE_COLOR, _diffuseColor); + READ_ENTITY_PROPERTY_COLOR(PROP_AMBIENT_COLOR, _ambientColor); + READ_ENTITY_PROPERTY_COLOR(PROP_SPECULAR_COLOR, _specularColor); + READ_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, float, _constantAttenuation); + READ_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, float, _linearAttenuation); + READ_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, float, _quadraticAttenuation); + READ_ENTITY_PROPERTY(PROP_EXPONENT, float, _exponent); + READ_ENTITY_PROPERTY(PROP_CUTOFF, float, _cutoff); + + return bytesRead; +} + + +// TODO: eventually only include properties changed since the params.lastViewFrustumSent time +EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + requestedProperties += PROP_IS_SPOTLIGHT; + requestedProperties += PROP_DIFFUSE_COLOR; + requestedProperties += PROP_AMBIENT_COLOR; + requestedProperties += PROP_SPECULAR_COLOR; + requestedProperties += PROP_CONSTANT_ATTENUATION; + requestedProperties += PROP_LINEAR_ATTENUATION; + requestedProperties += PROP_QUADRATIC_ATTENUATION; + requestedProperties += PROP_EXPONENT; + requestedProperties += PROP_CUTOFF; + return requestedProperties; +} + +void LightEntityItem::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_IS_SPOTLIGHT, appendValue, getIsSpotlight()); + APPEND_ENTITY_PROPERTY(PROP_DIFFUSE_COLOR, appendColor, getDiffuseColor()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_COLOR, appendColor, getAmbientColor()); + APPEND_ENTITY_PROPERTY(PROP_SPECULAR_COLOR, appendColor, getSpecularColor()); + APPEND_ENTITY_PROPERTY(PROP_CONSTANT_ATTENUATION, appendValue, getConstantAttenuation()); + APPEND_ENTITY_PROPERTY(PROP_LINEAR_ATTENUATION, appendValue, getLinearAttenuation()); + APPEND_ENTITY_PROPERTY(PROP_QUADRATIC_ATTENUATION, appendValue, getQuadraticAttenuation()); + APPEND_ENTITY_PROPERTY(PROP_EXPONENT, appendValue, getExponent()); + APPEND_ENTITY_PROPERTY(PROP_CUTOFF, appendValue, getCutoff()); +} diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h new file mode 100644 index 0000000000..2006efb896 --- /dev/null +++ b/libraries/entities/src/LightEntityItem.h @@ -0,0 +1,118 @@ +// +// LightEntityItem.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_LightEntityItem_h +#define hifi_LightEntityItem_h + +#include +#include "EntityItem.h" + +class LightEntityItem : public EntityItem { +public: + static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + LightEntityItem(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); + + 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); + + const rgbColor& getAmbientColor() const { return _ambientColor; } + xColor getAmbientXColor() const { + xColor color = { _ambientColor[RED_INDEX], _ambientColor[GREEN_INDEX], _ambientColor[BLUE_INDEX] }; return color; + } + + void setAmbientColor(const rgbColor& value) { memcpy(_ambientColor, value, sizeof(_ambientColor)); } + void setAmbientColor(const xColor& value) { + _ambientColor[RED_INDEX] = value.red; + _ambientColor[GREEN_INDEX] = value.green; + _ambientColor[BLUE_INDEX] = value.blue; + } + + const rgbColor& getDiffuseColor() const { return _diffuseColor; } + xColor getDiffuseXColor() const { + xColor color = { _diffuseColor[RED_INDEX], _diffuseColor[GREEN_INDEX], _diffuseColor[BLUE_INDEX] }; return color; + } + + void setDiffuseColor(const rgbColor& value) { memcpy(_diffuseColor, value, sizeof(_diffuseColor)); } + void setDiffuseColor(const xColor& value) { + _diffuseColor[RED_INDEX] = value.red; + _diffuseColor[GREEN_INDEX] = value.green; + _diffuseColor[BLUE_INDEX] = value.blue; + } + + const rgbColor& getSpecularColor() const { return _specularColor; } + xColor getSpecularXColor() const { + xColor color = { _specularColor[RED_INDEX], _specularColor[GREEN_INDEX], _specularColor[BLUE_INDEX] }; return color; + } + + void setSpecularColor(const rgbColor& value) { memcpy(_specularColor, value, sizeof(_specularColor)); } + void setSpecularColor(const xColor& value) { + _specularColor[RED_INDEX] = value.red; + _specularColor[GREEN_INDEX] = value.green; + _specularColor[BLUE_INDEX] = value.blue; + } + + bool getIsSpotlight() const { return _isSpotlight; } + void setIsSpotlight(bool value) { _isSpotlight = value; } + + float getConstantAttenuation() const { return _constantAttenuation; } + void setConstantAttenuation(float value) { _constantAttenuation = value; } + + float getLinearAttenuation() const { return _linearAttenuation; } + void setLinearAttenuation(float value) { _linearAttenuation = value; } + + float getQuadraticAttenuation() const { return _quadraticAttenuation; } + void setQuadraticAttenuation(float value) { _quadraticAttenuation = value; } + + float getExponent() const { return _exponent; } + void setExponent(float value) { _exponent = value; } + + float getCutoff() const { return _cutoff; } + void setCutoff(float value) { _cutoff = value; } + + virtual const Shape& getCollisionShapeInMeters() const { return _emptyShape; } + +protected: + virtual void recalculateCollisionShape() { /* nothing to do */ } + + // properties of a light + rgbColor _ambientColor; + rgbColor _diffuseColor; + rgbColor _specularColor; + bool _isSpotlight; + float _constantAttenuation; + float _linearAttenuation; + float _quadraticAttenuation; + float _exponent; + float _cutoff; + + // used for collision detection + SphereShape _emptyShape; +}; + +#endif // hifi_LightEntityItem_h diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 5da218c11a..f5b8eb27e9 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -33,10 +33,7 @@ SphereEntityItem::SphereEntityItem(const EntityItemID& entityItemID, const Entit EntityItemProperties SphereEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class - properties.setColor(getXColor()); - properties.setGlowLevel(getGlowLevel()); - return properties; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 2a51a83ab8..692aea2d6b 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include @@ -1534,7 +1533,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) joint.inverseBindRotation = joint.inverseDefaultRotation; joint.name = model.name; joint.shapePosition = glm::vec3(0.f); - joint.shapeType = UNKNOWN_SHAPE; + joint.shapeType = SHAPE_TYPE_UNKNOWN; foreach (const QString& childID, childMap.values(modelID)) { QString type = typeFlags.value(childID); @@ -1911,10 +1910,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) if (collideLikeCapsule) { joint.shapeRotation = rotationBetween(defaultCapsuleAxis, jointShapeInfo.boneBegin); joint.shapePosition = 0.5f * jointShapeInfo.boneBegin; - joint.shapeType = CAPSULE_SHAPE; + joint.shapeType = SHAPE_TYPE_CAPSULE; } else { // collide the joint like a sphere - joint.shapeType = SPHERE_SHAPE; + joint.shapeType = SHAPE_TYPE_SPHERE; if (jointShapeInfo.numVertices > 0) { jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices; joint.shapePosition = jointShapeInfo.averageVertex; @@ -1934,8 +1933,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) if (distanceFromEnd > joint.distanceToParent && distanceFromBegin > joint.distanceToParent) { // The shape is further from both joint endpoints than the endpoints are from each other // which probably means the model has a bad transform somewhere. We disable this shape - // by setting its type to UNKNOWN_SHAPE. - joint.shapeType = UNKNOWN_SHAPE; + // by setting its type to SHAPE_TYPE_UNKNOWN. + joint.shapeType = SHAPE_TYPE_UNKNOWN; } } } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 49b0534438..229d51e216 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -19,8 +19,6 @@ #include #include -#include - #include #include @@ -55,6 +53,12 @@ public: QVector normals; }; +enum ShapeType { + SHAPE_TYPE_SPHERE = 0, + SHAPE_TYPE_CAPSULE = 1, + SHAPE_TYPE_UNKNOWN = 2 +}; + /// A single joint (transformation node) extracted from an FBX document. class FBXJoint { public: @@ -79,7 +83,7 @@ public: QString name; glm::vec3 shapePosition; // in joint frame glm::quat shapeRotation; // in joint frame - Shape::Type shapeType; + ShapeType shapeType; bool isSkeletonJoint; }; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 1201a8973f..6113cadb06 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "NodeList.h" @@ -71,6 +72,11 @@ const QString AddressManager::currentPath(bool withOrientation) const { } } +QString AddressManager::getDomainID() const { + const QUuid& domainID = NodeList::getInstance()->getDomainHandler().getUUID(); + return domainID.isNull() ? "" : uuidStringWithoutCurlyBraces(domainID); +} + const JSONCallbackParameters& AddressManager::apiCallbackParameters() { static bool hasSetupParameters = false; static JSONCallbackParameters callbackParams; diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 8a25464223..d6ecdc9fc6 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -31,6 +31,7 @@ class AddressManager : public QObject { Q_PROPERTY(QString protocol READ getProtocol) Q_PROPERTY(QString hostname READ getCurrentDomain) Q_PROPERTY(QString pathname READ currentPath) + Q_PROPERTY(QString domainID READ getDomainID) public: static AddressManager& getInstance(); @@ -41,6 +42,7 @@ public: const QString currentPath(bool withOrientation = true) const; const QString& getCurrentDomain() const { return _currentDomain; } + QString getDomainID() const; void attemptPlaceNameLookup(const QString& lookupString); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 919dc75c23..5c4dc6cea2 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -19,10 +19,11 @@ #include #include +#include + #include "AccountManager.h" #include "Assignment.h" #include "HifiSockAddr.h" -#include "Logging.h" #include "LimitedNodeList.h" #include "PacketHeaders.h" #include "SharedUtil.h" @@ -211,8 +212,11 @@ bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) { << uuidFromPacketHeader(packet); } } else { + static QString repeatedMessage + = LogHandler::getInstance().addRepeatedMessageRegex("Packet of type \\d+ received from unknown node with UUID"); + qDebug() << "Packet of type" << checkType << "received from unknown node with UUID" - << uuidFromPacketHeader(packet); + << qPrintable(uuidStringWithoutCurlyBraces(uuidFromPacketHeader(packet))); } } else { return true; diff --git a/libraries/networking/src/Logging.cpp b/libraries/networking/src/Logging.cpp deleted file mode 100644 index f42f1bda58..0000000000 --- a/libraries/networking/src/Logging.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// -// Logging.cpp -// libraries/networking/src -// -// Created by Stephen Birarda on 6/11/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 -#include -//#include // not available on windows, apparently not needed on mac - -#ifdef _WIN32 -#include -#define getpid _getpid -#define getppid _getpid // hack to build -#define pid_t int // hack to build -#endif - -#include - -#include "HifiSockAddr.h" -#include "SharedUtil.h" -#include "NodeList.h" - -#include "Logging.h" - -HifiSockAddr Logging::_logstashSocket = HifiSockAddr(); -QString Logging::_targetName = QString(); - -const HifiSockAddr& Logging::socket() { - - if (_logstashSocket.getAddress().isNull()) { - // we need to construct the socket object - // use the constant port - _logstashSocket.setPort(htons(LOGSTASH_UDP_PORT)); - - // lookup the IP address for the constant hostname - QHostInfo hostInfo = QHostInfo::fromName(LOGSTASH_HOSTNAME); - if (!hostInfo.addresses().isEmpty()) { - // use the first IP address - _logstashSocket.setAddress(hostInfo.addresses().first()); - } else { - qDebug("Failed to lookup logstash IP - will try again on next log attempt."); - } - } - - return _logstashSocket; -} - -bool Logging::shouldSendStats() { - static bool shouldSendStats = isInEnvironment("production"); - return shouldSendStats; -} - -void Logging::stashValue(char statType, const char* key, float value) { - static char logstashPacket[MAX_PACKET_SIZE]; - - // load up the logstash packet with the key and the passed float value - // send it to 4 decimal places - int numPacketBytes = sprintf(logstashPacket, "%c %s %.4f", statType, key, value); - - NodeList *nodeList = NodeList::getInstance(); - - if (nodeList) { - nodeList->getNodeSocket().writeDatagram(logstashPacket, numPacketBytes, - _logstashSocket.getAddress(), _logstashSocket.getPort()); - } -} - -const char* stringForLogType(QtMsgType msgType) { - switch (msgType) { - case QtDebugMsg: - return "DEBUG"; - case QtCriticalMsg: - return "CRITICAL"; - case QtFatalMsg: - return "FATAL"; - case QtWarningMsg: - return "WARNING"; - default: - return "UNKNOWN"; - } -} - -// the following will produce 2000-10-02 13:55:36 -0700 -const char DATE_STRING_FORMAT[] = "%Y-%m-%d %H:%M:%S %z"; - -void Logging::verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - if (message.isEmpty()) { - return; - } - // log prefix is in the following format - // [DEBUG] [TIMESTAMP] [PID:PARENT_PID] [TARGET] logged string - - QString prefixString = QString("[%1]").arg(stringForLogType(type)); - - time_t rawTime; - time(&rawTime); - struct tm* localTime = localtime(&rawTime); - - char dateString[100]; - strftime(dateString, sizeof(dateString), DATE_STRING_FORMAT, localTime); - - prefixString.append(QString(" [%1]").arg(dateString)); - - prefixString.append(QString(" [%1").arg(getpid())); - - pid_t parentProcessID = getppid(); - if (parentProcessID != 0) { - prefixString.append(QString(":%1]").arg(parentProcessID)); - } else { - prefixString.append("]"); - } - - if (!_targetName.isEmpty()) { - prefixString.append(QString(" [%1]").arg(_targetName)); - } - - fprintf(stdout, "%s %s\n", prefixString.toLocal8Bit().constData(), message.toLocal8Bit().constData()); -} diff --git a/libraries/networking/src/Logging.h b/libraries/networking/src/Logging.h deleted file mode 100644 index c52812bd33..0000000000 --- a/libraries/networking/src/Logging.h +++ /dev/null @@ -1,54 +0,0 @@ -// -// Logging.h -// libraries/networking/src -// -// Created by Stephen Birarda on 6/11/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_Logging_h -#define hifi_Logging_h - -#include - -const int LOGSTASH_UDP_PORT = 9500; -const char LOGSTASH_HOSTNAME[] = "graphite.highfidelity.io"; - -const char STAT_TYPE_TIMER = 't'; -const char STAT_TYPE_COUNTER = 'c'; -const char STAT_TYPE_GAUGE = 'g'; - -class HifiSockAddr; - -/// Handles custom message handling and sending of stats/logs to Logstash instance -class Logging { -public: - /// \return the socket used to send stats to logstash - static const HifiSockAddr& socket(); - - /// checks if this target should send stats to logstash, given its current environment - /// \return true if the caller should send stats to logstash - static bool shouldSendStats(); - - /// stashes a float value to Logstash instance - /// \param statType a stat type from the constants in this file - /// \param key the key at which to store the stat - /// \param value the value to store - static void stashValue(char statType, const char* key, float value); - - /// sets the target name to output via the verboseMessageHandler, called once before logging begins - /// \param targetName the desired target name to output in logs - static void setTargetName(const QString& targetName) { _targetName = targetName; } - - /// a qtMessageHandler that can be hooked up to a target that links to Qt - /// prints various process, message type, and time information - static void verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message); -private: - static HifiSockAddr _logstashSocket; - static QString _targetName; -}; - -#endif // hifi_Logging_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index feeb13500a..ff3b86880d 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -15,10 +15,11 @@ #include #include +#include + #include "AccountManager.h" #include "Assignment.h" #include "HifiSockAddr.h" -#include "Logging.h" #include "NodeList.h" #include "PacketHeaders.h" #include "SharedUtil.h" diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 8ce379b203..73671295df 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -53,7 +53,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeSilentAudioFrame: return 4; case PacketTypeMixedAudio: - return 2; + return 1; case PacketTypeAvatarData: return 3; case PacketTypeAvatarIdentity: @@ -71,11 +71,9 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeOctreeStats: return 1; - case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: return VERSION_ENTITIES_SUPPORT_DIMENSIONS; - case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: @@ -135,8 +133,13 @@ QString nameForPacketType(PacketType type) { PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAddResponse); PACKET_TYPE_NAME_LOOKUP(PacketTypeOctreeDataNack); PACKET_TYPE_NAME_LOOKUP(PacketTypeVoxelEditNack); + PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioEnvironment); PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityEditNack); PACKET_TYPE_NAME_LOOKUP(PacketTypeSignedTransactionPayment); + PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeat); + PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeatResponse); + PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPing); + PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPingReply); default: return QString("Type: ") + QString::number((int)type); } diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 7632f47686..2e9ce697f0 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -68,7 +68,7 @@ enum PacketType { PacketTypeEntityAddResponse, PacketTypeOctreeDataNack, // 45 PacketTypeVoxelEditNack, - UNUSED_6, + PacketTypeAudioEnvironment, PacketTypeEntityEditNack, // 48 PacketTypeSignedTransactionPayment, PacketTypeIceServerHeartbeat, diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 6d2e366499..52644a9a4e 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -14,7 +14,8 @@ #include #include -#include "Logging.h" +#include + #include "ThreadedAssignment.h" ThreadedAssignment::ThreadedAssignment(const QByteArray& packet) : @@ -54,7 +55,7 @@ void ThreadedAssignment::setFinished(bool isFinished) { void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeType, bool shouldSendStats) { // change the logging target name while the assignment is running - Logging::setTargetName(targetName); + LogHandler::getInstance().setTargetName(targetName); NodeList* nodeList = NodeList::getInstance(); nodeList->setOwnerType(nodeType); diff --git a/libraries/octree/CMakeLists.txt b/libraries/octree/CMakeLists.txt index c302a082be..9aea2fdea1 100644 --- a/libraries/octree/CMakeLists.txt +++ b/libraries/octree/CMakeLists.txt @@ -5,7 +5,7 @@ setup_hifi_library() include_glm() -link_hifi_libraries(shared networking) +link_hifi_libraries(shared networking physics) # find ZLIB find_package(ZLIB REQUIRED) @@ -16,4 +16,4 @@ include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}") list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${ZLIB_LIBRARIES}") # call macro to link our dependencies and bubble them up via a property on our target -link_shared_dependencies() \ No newline at end of file +link_shared_dependencies() diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index e07d2e2688..da0e483e1e 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -31,8 +31,6 @@ class Shape; #include "OctreePacketData.h" #include "OctreeSceneStats.h" -#include - #include #include #include diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 4b2b57fab2..f1894c7cab 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -24,7 +24,7 @@ #include "OctreeProjectedPolygon.h" const float DEFAULT_KEYHOLE_RADIUS = 3.0f; -const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f; +const float DEFAULT_FIELD_OF_VIEW_DEGREES = 45.0f; const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.f; const float DEFAULT_ASPECT_RATIO = 16.f/9.f; const float DEFAULT_NEAR_CLIP = 0.08f; diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt new file mode 100644 index 0000000000..5270f08730 --- /dev/null +++ b/libraries/physics/CMakeLists.txt @@ -0,0 +1,19 @@ +set(TARGET_NAME physics) + +# use setup_hifi_library macro to setup our project and link appropriate Qt modules +setup_hifi_library() + +include_glm() + +link_hifi_libraries(shared) + +## find BULLET +#find_package(BULLET REQUIRED) +# +#include_directories(SYSTEM "${BULLET_INCLUDE_DIRS}") +# +## append BULLET to our list of libraries to link +#list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${BULLET_LIBRARIES}") + +# call macro to link our dependencies and bubble them up via a property on our target +link_shared_dependencies() diff --git a/libraries/shared/src/AACubeShape.cpp b/libraries/physics/src/AACubeShape.cpp similarity index 98% rename from libraries/shared/src/AACubeShape.cpp rename to libraries/physics/src/AACubeShape.cpp index 30197d6bfd..fa1a45b809 100644 --- a/libraries/shared/src/AACubeShape.cpp +++ b/libraries/physics/src/AACubeShape.cpp @@ -13,7 +13,7 @@ #include #include "AACubeShape.h" -#include "SharedUtil.h" // for SQUARE_ROOT_OF_3 +#include // for SQUARE_ROOT_OF_3 glm::vec3 faceNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) }; diff --git a/libraries/shared/src/AACubeShape.h b/libraries/physics/src/AACubeShape.h similarity index 100% rename from libraries/shared/src/AACubeShape.h rename to libraries/physics/src/AACubeShape.h diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/physics/src/CapsuleShape.cpp similarity index 99% rename from libraries/shared/src/CapsuleShape.cpp rename to libraries/physics/src/CapsuleShape.cpp index 5bb118d36e..778798c15b 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/physics/src/CapsuleShape.cpp @@ -12,10 +12,10 @@ #include #include -#include "CapsuleShape.h" +#include +#include -#include "GeometryUtil.h" -#include "SharedUtil.h" +#include "CapsuleShape.h" CapsuleShape::CapsuleShape() : Shape(CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {} diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/physics/src/CapsuleShape.h similarity index 98% rename from libraries/shared/src/CapsuleShape.h rename to libraries/physics/src/CapsuleShape.h index 6e889f6566..ede6993b40 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/physics/src/CapsuleShape.h @@ -12,9 +12,9 @@ #ifndef hifi_CapsuleShape_h #define hifi_CapsuleShape_h -#include "Shape.h" +#include -#include "SharedUtil.h" +#include "Shape.h" // default axis of CapsuleShape is Y-axis const glm::vec3 DEFAULT_CAPSULE_AXIS(0.0f, 1.0f, 0.0f); diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/physics/src/CollisionInfo.cpp similarity index 99% rename from libraries/shared/src/CollisionInfo.cpp rename to libraries/physics/src/CollisionInfo.cpp index 9dc321fa44..a652f3ce41 100644 --- a/libraries/shared/src/CollisionInfo.cpp +++ b/libraries/physics/src/CollisionInfo.cpp @@ -9,10 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "CollisionInfo.h" +#include + +#include "CollisionInfo.h" #include "Shape.h" -#include "SharedUtil.h" CollisionInfo::CollisionInfo() : _data(NULL), diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/physics/src/CollisionInfo.h similarity index 100% rename from libraries/shared/src/CollisionInfo.h rename to libraries/physics/src/CollisionInfo.h diff --git a/libraries/shared/src/Constraint.h b/libraries/physics/src/Constraint.h similarity index 100% rename from libraries/shared/src/Constraint.h rename to libraries/physics/src/Constraint.h diff --git a/libraries/physics/src/ContactConstraint.cpp b/libraries/physics/src/ContactConstraint.cpp new file mode 100644 index 0000000000..9d1a92bb21 --- /dev/null +++ b/libraries/physics/src/ContactConstraint.cpp @@ -0,0 +1,53 @@ +// +// ContactConstraint.cpp +// interface/src/avatar +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "ContactConstraint.h" +#include "VerletPoint.h" + + +ContactConstraint::ContactConstraint(VerletPoint* pointA, VerletPoint* pointB) + : _pointA(pointA), _pointB(pointB), _strength(1.0f) { + assert(_pointA != NULL && _pointB != NULL); + _offset = _pointB->_position - _pointA->_position; +} + +float ContactConstraint::enforce() { + _pointB->_position += _strength * (_pointA->_position + _offset - _pointB->_position); + return 0.0f; +} + +float ContactConstraint::enforceWithNormal(const glm::vec3& normal) { + glm::vec3 delta = _pointA->_position + _offset - _pointB->_position; + + // split delta into parallel (pDelta) and perpendicular (qDelta) components + glm::vec3 pDelta = glm::dot(delta, normal) * normal; + glm::vec3 qDelta = delta - pDelta; + + // use the relative sizes of the components to decide how much perpenducular delta to use + // (i.e. dynamic friction) + float lpDelta = glm::length(pDelta); + float lqDelta = glm::length(qDelta); + float qFactor = lqDelta > lpDelta ? (lpDelta / lqDelta - 1.0f) : 0.0f; + // recombine the two components to get the final delta + delta = pDelta + qFactor * qDelta; + + // attenuate strength by how much _offset is perpendicular to normal + float distance = glm::length(_offset); + float strength = _strength * ((distance > EPSILON) ? glm::abs(glm::dot(_offset, normal)) / distance : 1.0f); + + // move _pointB + _pointB->_position += strength * delta; + + return strength * glm::length(delta); +} + diff --git a/libraries/physics/src/ContactConstraint.h b/libraries/physics/src/ContactConstraint.h new file mode 100644 index 0000000000..41be2f769d --- /dev/null +++ b/libraries/physics/src/ContactConstraint.h @@ -0,0 +1,39 @@ +// +// ContactConstraint.h +// interface/src/avatar +// +// Created by Andrew Meadows 2014.07.24 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ContactConstraint_h +#define hifi_ContactConstraint_h + +#include + +#include "Constraint.h" +#include "VerletPoint.h" + +class ContactConstraint : public Constraint { +public: + ContactConstraint(VerletPoint* pointA, VerletPoint* pointB); + + float enforce(); + float enforceWithNormal(const glm::vec3& normal); + + glm::vec3 getTargetPointA() const { return _pointB->_position - _offset; } + + void setOffset(const glm::vec3& offset) { _offset = offset; } + void setStrength(float strength) { _strength = glm::clamp(strength, 0.0f, 1.0f); } + float getStrength() const { return _strength; } +private: + VerletPoint* _pointA; + VerletPoint* _pointB; + glm::vec3 _offset; // from pointA toward pointB + float _strength; // a value in range [0,1] +}; + +#endif // hifi_ContactConstraint_h diff --git a/libraries/shared/src/ContactPoint.cpp b/libraries/physics/src/ContactPoint.cpp similarity index 99% rename from libraries/shared/src/ContactPoint.cpp rename to libraries/physics/src/ContactPoint.cpp index 02cf896594..b9ad87aa8f 100644 --- a/libraries/shared/src/ContactPoint.cpp +++ b/libraries/physics/src/ContactPoint.cpp @@ -9,9 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "ContactPoint.h" #include "Shape.h" -#include "SharedUtil.h" // This parameter helps keep the actual point of contact slightly inside each shape // which allows the collisions to happen almost every frame for more frequent updates. diff --git a/libraries/shared/src/ContactPoint.h b/libraries/physics/src/ContactPoint.h similarity index 100% rename from libraries/shared/src/ContactPoint.h rename to libraries/physics/src/ContactPoint.h diff --git a/libraries/shared/src/DistanceConstraint.cpp b/libraries/physics/src/DistanceConstraint.cpp similarity index 97% rename from libraries/shared/src/DistanceConstraint.cpp rename to libraries/physics/src/DistanceConstraint.cpp index 50fa09d307..94dbfeba24 100644 --- a/libraries/shared/src/DistanceConstraint.cpp +++ b/libraries/physics/src/DistanceConstraint.cpp @@ -9,8 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include // for EPSILON + #include "DistanceConstraint.h" -#include "SharedUtil.h" // for EPSILON #include "VerletPoint.h" DistanceConstraint::DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint) : _distance(-1.0f) { diff --git a/libraries/shared/src/DistanceConstraint.h b/libraries/physics/src/DistanceConstraint.h similarity index 100% rename from libraries/shared/src/DistanceConstraint.h rename to libraries/physics/src/DistanceConstraint.h diff --git a/libraries/shared/src/FixedConstraint.cpp b/libraries/physics/src/FixedConstraint.cpp similarity index 100% rename from libraries/shared/src/FixedConstraint.cpp rename to libraries/physics/src/FixedConstraint.cpp diff --git a/libraries/shared/src/FixedConstraint.h b/libraries/physics/src/FixedConstraint.h similarity index 100% rename from libraries/shared/src/FixedConstraint.h rename to libraries/physics/src/FixedConstraint.h diff --git a/libraries/shared/src/ListShape.cpp b/libraries/physics/src/ListShape.cpp similarity index 100% rename from libraries/shared/src/ListShape.cpp rename to libraries/physics/src/ListShape.cpp diff --git a/libraries/shared/src/ListShape.h b/libraries/physics/src/ListShape.h similarity index 100% rename from libraries/shared/src/ListShape.h rename to libraries/physics/src/ListShape.h diff --git a/libraries/shared/src/PhysicsEntity.cpp b/libraries/physics/src/PhysicsEntity.cpp similarity index 100% rename from libraries/shared/src/PhysicsEntity.cpp rename to libraries/physics/src/PhysicsEntity.cpp diff --git a/libraries/shared/src/PhysicsEntity.h b/libraries/physics/src/PhysicsEntity.h similarity index 100% rename from libraries/shared/src/PhysicsEntity.h rename to libraries/physics/src/PhysicsEntity.h diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/physics/src/PhysicsSimulation.cpp similarity index 99% rename from libraries/shared/src/PhysicsSimulation.cpp rename to libraries/physics/src/PhysicsSimulation.cpp index ee5ea9b2b8..72a1eeebfd 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/physics/src/PhysicsSimulation.cpp @@ -11,14 +11,14 @@ #include -#include "PhysicsSimulation.h" +#include +#include -#include "PerfStat.h" +#include "PhysicsSimulation.h" #include "PhysicsEntity.h" #include "Ragdoll.h" #include "Shape.h" #include "ShapeCollider.h" -#include "SharedUtil.h" int MAX_DOLLS_PER_SIMULATION = 16; int MAX_ENTITIES_PER_SIMULATION = 64; diff --git a/libraries/shared/src/PhysicsSimulation.h b/libraries/physics/src/PhysicsSimulation.h similarity index 100% rename from libraries/shared/src/PhysicsSimulation.h rename to libraries/physics/src/PhysicsSimulation.h diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/physics/src/PlaneShape.cpp similarity index 98% rename from libraries/shared/src/PlaneShape.cpp rename to libraries/physics/src/PlaneShape.cpp index 845b58728a..1a4122f3aa 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/physics/src/PlaneShape.cpp @@ -9,9 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include + #include "PlaneShape.h" -#include "SharedUtil.h" -#include "GLMHelpers.h" const glm::vec3 UNROTATED_NORMAL(0.0f, 1.0f, 0.0f); diff --git a/libraries/shared/src/PlaneShape.h b/libraries/physics/src/PlaneShape.h similarity index 100% rename from libraries/shared/src/PlaneShape.h rename to libraries/physics/src/PlaneShape.h diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/physics/src/Ragdoll.cpp similarity index 99% rename from libraries/shared/src/Ragdoll.cpp rename to libraries/physics/src/Ragdoll.cpp index c0f0eb4b27..3eecef2333 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/physics/src/Ragdoll.cpp @@ -11,13 +11,14 @@ #include +#include // for EPSILON + #include "Ragdoll.h" #include "Constraint.h" #include "DistanceConstraint.h" #include "FixedConstraint.h" #include "PhysicsSimulation.h" -#include "SharedUtil.h" // for EPSILON Ragdoll::Ragdoll() : _massScale(1.0f), _translation(0.0f), _translationInSimulationFrame(0.0f), _rootIndex(0), _accumulatedMovement(0.0f), _simulation(NULL) { diff --git a/libraries/shared/src/Ragdoll.h b/libraries/physics/src/Ragdoll.h similarity index 100% rename from libraries/shared/src/Ragdoll.h rename to libraries/physics/src/Ragdoll.h index 5234397833..5447b6769e 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/physics/src/Ragdoll.h @@ -14,10 +14,10 @@ #include #include -#include "VerletPoint.h" #include +#include "VerletPoint.h" //#include "PhysicsSimulation.h" class DistanceConstraint; diff --git a/libraries/shared/src/RayIntersectionInfo.h b/libraries/physics/src/RayIntersectionInfo.h similarity index 100% rename from libraries/shared/src/RayIntersectionInfo.h rename to libraries/physics/src/RayIntersectionInfo.h diff --git a/libraries/shared/src/Shape.h b/libraries/physics/src/Shape.h similarity index 100% rename from libraries/shared/src/Shape.h rename to libraries/physics/src/Shape.h diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/physics/src/ShapeCollider.cpp similarity index 99% rename from libraries/shared/src/ShapeCollider.cpp rename to libraries/physics/src/ShapeCollider.cpp index 3f79fa081a..2f8ea88553 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/physics/src/ShapeCollider.cpp @@ -11,16 +11,17 @@ #include +#include +#include + #include "ShapeCollider.h" #include "AACubeShape.h" #include "CapsuleShape.h" -#include "GeometryUtil.h" #include "ListShape.h" #include "PlaneShape.h" #include "SphereShape.h" -#include "StreamUtils.h" // NOTE: // diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/physics/src/ShapeCollider.h similarity index 99% rename from libraries/shared/src/ShapeCollider.h rename to libraries/physics/src/ShapeCollider.h index 618a5ba115..7414665ca7 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/physics/src/ShapeCollider.h @@ -14,9 +14,10 @@ #include +#include + #include "CollisionInfo.h" #include "RayIntersectionInfo.h" -#include "SharedUtil.h" class Shape; class SphereShape; diff --git a/libraries/shared/src/SphereShape.cpp b/libraries/physics/src/SphereShape.cpp similarity index 100% rename from libraries/shared/src/SphereShape.cpp rename to libraries/physics/src/SphereShape.cpp diff --git a/libraries/shared/src/SphereShape.h b/libraries/physics/src/SphereShape.h similarity index 98% rename from libraries/shared/src/SphereShape.h rename to libraries/physics/src/SphereShape.h index 59a53c97d6..72f46c1168 100644 --- a/libraries/shared/src/SphereShape.h +++ b/libraries/physics/src/SphereShape.h @@ -12,9 +12,10 @@ #ifndef hifi_SphereShape_h #define hifi_SphereShape_h +#include + #include "Shape.h" -#include "SharedUtil.h" class SphereShape : public Shape { public: diff --git a/libraries/shared/src/VerletCapsuleShape.cpp b/libraries/physics/src/VerletCapsuleShape.cpp similarity index 99% rename from libraries/shared/src/VerletCapsuleShape.cpp rename to libraries/physics/src/VerletCapsuleShape.cpp index ce324a781a..78e3f6763b 100644 --- a/libraries/shared/src/VerletCapsuleShape.cpp +++ b/libraries/physics/src/VerletCapsuleShape.cpp @@ -9,10 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "VerletCapsuleShape.h" -#include "Ragdoll.h" // for VerletPoint -#include "SharedUtil.h" +#include "VerletPoint.h" VerletCapsuleShape::VerletCapsuleShape(VerletPoint* startPoint, VerletPoint* endPoint) : CapsuleShape(), _startPoint(startPoint), _endPoint(endPoint), _startLagrangeCoef(0.5f), _endLagrangeCoef(0.5f) { diff --git a/libraries/shared/src/VerletCapsuleShape.h b/libraries/physics/src/VerletCapsuleShape.h similarity index 100% rename from libraries/shared/src/VerletCapsuleShape.h rename to libraries/physics/src/VerletCapsuleShape.h diff --git a/libraries/shared/src/VerletPoint.cpp b/libraries/physics/src/VerletPoint.cpp similarity index 100% rename from libraries/shared/src/VerletPoint.cpp rename to libraries/physics/src/VerletPoint.cpp diff --git a/libraries/shared/src/VerletPoint.h b/libraries/physics/src/VerletPoint.h similarity index 100% rename from libraries/shared/src/VerletPoint.h rename to libraries/physics/src/VerletPoint.h diff --git a/libraries/shared/src/VerletSphereShape.cpp b/libraries/physics/src/VerletSphereShape.cpp similarity index 100% rename from libraries/shared/src/VerletSphereShape.cpp rename to libraries/physics/src/VerletSphereShape.cpp diff --git a/libraries/shared/src/VerletSphereShape.h b/libraries/physics/src/VerletSphereShape.h similarity index 100% rename from libraries/shared/src/VerletSphereShape.h rename to libraries/physics/src/VerletSphereShape.h diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 166ee8c50e..3b3a63549d 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -5,7 +5,7 @@ setup_hifi_library(Gui Network Script Widgets) include_glm() -link_hifi_libraries(shared octree voxels fbx entities animation audio) +link_hifi_libraries(shared octree voxels fbx entities animation audio physics) # call macro to link our dependencies and bubble them up via a property on our target link_shared_dependencies() diff --git a/libraries/script-engine/src/UndoStackScriptingInterface.cpp b/libraries/script-engine/src/UndoStackScriptingInterface.cpp index ed0f4d563d..17bf8b1aa6 100644 --- a/libraries/script-engine/src/UndoStackScriptingInterface.cpp +++ b/libraries/script-engine/src/UndoStackScriptingInterface.cpp @@ -30,6 +30,7 @@ void UndoStackScriptingInterface::pushCommand(QScriptValue undoFunction, QScript ScriptUndoCommand::ScriptUndoCommand(QScriptValue undoFunction, QScriptValue undoData, QScriptValue redoFunction, QScriptValue redoData) : + _hasRedone(false), _undoFunction(undoFunction), _undoData(undoData), _redoFunction(redoFunction), @@ -41,7 +42,15 @@ void ScriptUndoCommand::undo() { } void ScriptUndoCommand::redo() { - QMetaObject::invokeMethod(this, "doRedo"); + // QUndoStack will call `redo()` when adding a command to the stack. This + // makes it difficult to work with commands that span a period of time - for instance, + // the entity duplicate + move command that duplicates an entity and then moves it. + // A better implementation might be to properly implement `mergeWith()` and `id()` + // so that the two actions in the example would be merged. + if (_hasRedone) { + QMetaObject::invokeMethod(this, "doRedo"); + } + _hasRedone = true; } void ScriptUndoCommand::doUndo() { @@ -50,7 +59,6 @@ void ScriptUndoCommand::doUndo() { _undoFunction.call(QScriptValue(), args); } - void ScriptUndoCommand::doRedo() { QScriptValueList args; args << _redoData; diff --git a/libraries/script-engine/src/UndoStackScriptingInterface.h b/libraries/script-engine/src/UndoStackScriptingInterface.h index 835e5dfff4..479648fc92 100644 --- a/libraries/script-engine/src/UndoStackScriptingInterface.h +++ b/libraries/script-engine/src/UndoStackScriptingInterface.h @@ -43,6 +43,7 @@ public slots: void doRedo(); private: + bool _hasRedone; QScriptValue _undoFunction; QScriptValue _undoData; QScriptValue _redoFunction; diff --git a/libraries/shared/src/AngularConstraint.cpp b/libraries/shared/src/AngularConstraint.cpp index b39823ee3b..369885c91d 100644 --- a/libraries/shared/src/AngularConstraint.cpp +++ b/libraries/shared/src/AngularConstraint.cpp @@ -11,9 +11,8 @@ #include -#include "GLMHelpers.h" - #include "AngularConstraint.h" +#include "GLMHelpers.h" // helper function /// \param angle radian angle to be clamped within angleMin and angleMax diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp new file mode 100644 index 0000000000..7b06b77ae8 --- /dev/null +++ b/libraries/shared/src/LogHandler.cpp @@ -0,0 +1,144 @@ +// +// LogHandler.cpp +// libraries/shared/src +// +// Created by Stephen Birarda on 2014-10-28. +// Migrated from Logging.cpp created on 6/11/13 +// 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 + +#ifdef _WIN32 +#include +#define getpid _getpid +#define getppid _getpid // hack to build +#define pid_t int // hack to build +#endif + +#include +#include + +#include "LogHandler.h" + +LogHandler& LogHandler::getInstance() { + static LogHandler staticInstance; + return staticInstance; +} + +LogHandler::LogHandler() : + _shouldOutputPID(false) +{ + // setup our timer to flush the verbose logs every 5 seconds + QTimer* logFlushTimer = new QTimer(this); + connect(logFlushTimer, &QTimer::timeout, this, &LogHandler::flushRepeatedMessages); + logFlushTimer->start(VERBOSE_LOG_INTERVAL_SECONDS * 1000); +} + +const char* stringForLogType(LogMsgType msgType) { + switch (msgType) { + case QtDebugMsg: + return "DEBUG"; + case QtCriticalMsg: + return "CRITICAL"; + case QtFatalMsg: + return "FATAL"; + case QtWarningMsg: + return "WARNING"; + case LogSuppressed: + return "SUPPRESS"; + default: + return "UNKNOWN"; + } +} + +// the following will produce 2000-10-02 13:55:36 -0700 +const char DATE_STRING_FORMAT[] = "%Y-%m-%d %H:%M:%S %z"; + +void LogHandler::flushRepeatedMessages() { + QHash::iterator message = _repeatMessageCountHash.begin(); + while (message != _repeatMessageCountHash.end()) { + + if (message.value() > 0) { + QString repeatMessage = QString("%1 repeated log entries matching \"%2\" - Last entry: \"%3\"") + .arg(message.value()).arg(message.key()).arg(_lastRepeatedMessage.value(message.key())); + + QMessageLogContext emptyContext; + printMessage(LogSuppressed, emptyContext, repeatMessage); + } + + _lastRepeatedMessage.remove(message.key()); + message = _repeatMessageCountHash.erase(message); + } +} + +QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& context, const QString& message) { + + if (message.isEmpty()) { + return QString(); + } + + if (type == LogDebug) { + // for debug messages, check if this matches any of our regexes for repeated log messages + foreach(const QString& regexString, getInstance()._repeatedMessageRegexes) { + QRegExp repeatRegex(regexString); + if (repeatRegex.indexIn(message) != -1) { + + if (!_repeatMessageCountHash.contains(regexString)) { + // we have a match but didn't have this yet - output the first one + _repeatMessageCountHash[regexString] = 0; + + // break the foreach so we output the first match + break; + } else { + // we have a match - add 1 to the count of repeats for this message and set this as the last repeated message + _repeatMessageCountHash[regexString] += 1; + _lastRepeatedMessage[regexString] = message; + + // return out, we're not printing this one + return QString(); + } + } + } + } + + // log prefix is in the following format + // [DEBUG] [TIMESTAMP] [PID:PARENT_PID] [TARGET] logged string + + QString prefixString = QString("[%1]").arg(stringForLogType(type)); + + time_t rawTime; + time(&rawTime); + struct tm* localTime = localtime(&rawTime); + + char dateString[100]; + strftime(dateString, sizeof(dateString), DATE_STRING_FORMAT, localTime); + + prefixString.append(QString(" [%1]").arg(dateString)); + + if (_shouldOutputPID) { + prefixString.append(QString(" [%1").arg(getpid())); + + pid_t parentProcessID = getppid(); + if (parentProcessID != 0) { + prefixString.append(QString(":%1]").arg(parentProcessID)); + } else { + prefixString.append("]"); + } + } + + if (!_targetName.isEmpty()) { + prefixString.append(QString(" [%1]").arg(_targetName)); + } + + QString logMessage = QString("%1 %2").arg(prefixString, message); + fprintf(stdout, "%s\n", qPrintable(logMessage)); + return logMessage; +} + +void LogHandler::verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { + getInstance().printMessage((LogMsgType) type, context, message); +} diff --git a/libraries/shared/src/LogHandler.h b/libraries/shared/src/LogHandler.h new file mode 100644 index 0000000000..f8f06b0033 --- /dev/null +++ b/libraries/shared/src/LogHandler.h @@ -0,0 +1,63 @@ +// +// LogHandler.cpp +// libraries/shared/src +// +// Created by Stephen Birarda on 2014-10-28. +// Migrated from Logging.cpp created on 6/11/13 +// 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_LogHandler_h +#define hifi_LogHandler_h + +#include +#include +#include +#include +#include + +const int VERBOSE_LOG_INTERVAL_SECONDS = 5; + +enum LogMsgType { + LogDebug, + LogWarning, + LogCritical, + LogFatal, + LogSuppressed +}; + +/// Handles custom message handling and sending of stats/logs to Logstash instance +class LogHandler : public QObject { + Q_OBJECT +public: + static LogHandler& getInstance(); + + /// sets the target name to output via the verboseMessageHandler, called once before logging begins + /// \param targetName the desired target name to output in logs + void setTargetName(const QString& targetName) { _targetName = targetName; } + + void setShouldOutputPID(bool shouldOutputPID) { _shouldOutputPID = shouldOutputPID; } + + QString printMessage(LogMsgType type, const QMessageLogContext& context, const QString &message); + + /// a qtMessageHandler that can be hooked up to a target that links to Qt + /// prints various process, message type, and time information + static void verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message); + + const QString& addRepeatedMessageRegex(const QString& regexString) { return *_repeatedMessageRegexes.insert(regexString); } +private: + LogHandler(); + + void flushRepeatedMessages(); + + QString _targetName; + bool _shouldOutputPID; + QSet _repeatedMessageRegexes; + QHash _repeatMessageCountHash; + QHash _lastRepeatedMessage; +}; + +#endif // hifi_LogHandler_h \ No newline at end of file diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 7dafde9e43..5867e2ef43 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -13,6 +13,8 @@ #include #include +#include + #include "RegisteredMetaTypes.h" static int vec4MetaTypeId = qRegisterMetaType(); @@ -21,7 +23,7 @@ static int vec2MetaTypeId = qRegisterMetaType(); static int quatMetaTypeId = qRegisterMetaType(); static int xColorMetaTypeId = qRegisterMetaType(); static int pickRayMetaTypeId = qRegisterMetaType(); -static int collisionMetaTypeId = qRegisterMetaType(); +static int collisionMetaTypeId = qRegisterMetaType(); void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); @@ -163,14 +165,14 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay) { } } -QScriptValue collisionToScriptValue(QScriptEngine* engine, const CollisionInfo& collision) { +QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision) { QScriptValue obj = engine->newObject(); - obj.setProperty("penetration", vec3toScriptValue(engine, collision._penetration)); - obj.setProperty("contactPoint", vec3toScriptValue(engine, collision._contactPoint)); + obj.setProperty("penetration", vec3toScriptValue(engine, collision.penetration)); + obj.setProperty("contactPoint", vec3toScriptValue(engine, collision.contactPoint)); return obj; } -void collisionFromScriptValue(const QScriptValue &object, CollisionInfo& collision) { +void collisionFromScriptValue(const QScriptValue &object, Collision& collision) { // TODO: implement this when we know what it means to accept collision events from JS } diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index a1c6fdf710..7fe662740a 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -12,11 +12,11 @@ #ifndef hifi_RegisteredMetaTypes_h #define hifi_RegisteredMetaTypes_h -#include - #include -#include "CollisionInfo.h" +#include +#include + #include "SharedUtil.h" class QColor; @@ -53,7 +53,7 @@ void qURLFromScriptValue(const QScriptValue& object, QUrl& url); class PickRay { public: - PickRay() : origin(0), direction(0) { }; + PickRay() : origin(0.0f), direction(0.0f) { } glm::vec3 origin; glm::vec3 direction; }; @@ -61,9 +61,15 @@ Q_DECLARE_METATYPE(PickRay) QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay); void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay); -Q_DECLARE_METATYPE(CollisionInfo) -QScriptValue collisionToScriptValue(QScriptEngine* engine, const CollisionInfo& collision); -void collisionFromScriptValue(const QScriptValue &object, CollisionInfo& collision); +class Collision { +public: + Collision() : contactPoint(0.0f), penetration(0.0f) { } + glm::vec3 contactPoint; + glm::vec3 penetration; +}; +Q_DECLARE_METATYPE(Collision) +QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision); +void collisionFromScriptValue(const QScriptValue &object, Collision& collision); //Q_DECLARE_METATYPE(QUuid) // don't need to do this for QUuid since it's already a meta type QScriptValue quuidToScriptValue(QScriptEngine* engine, const QUuid& uuid); diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index e19a7c87e6..7139d4edb6 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -5,6 +5,6 @@ setup_hifi_project(Script Network) include_glm() # link in the shared libraries -link_hifi_libraries(shared octree voxels fbx metavoxels networking entities avatars audio animation script-engine) +link_hifi_libraries(shared octree voxels fbx metavoxels networking entities avatars audio animation script-engine physics) link_shared_dependencies() diff --git a/tests/physics/CMakeLists.txt b/tests/physics/CMakeLists.txt index 96aaf48860..d47b979459 100644 --- a/tests/physics/CMakeLists.txt +++ b/tests/physics/CMakeLists.txt @@ -5,6 +5,6 @@ setup_hifi_project() include_glm() # link in the shared libraries -link_hifi_libraries(shared) +link_hifi_libraries(shared physics) -link_shared_dependencies() \ No newline at end of file +link_shared_dependencies()