diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 5bc4829117..c7b8ab5732 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -9,11 +9,12 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") find_package(Qt5Network REQUIRED) +find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiProject.cmake) setup_hifi_project(${TARGET_NAME} TRUE) -qt5_use_modules(${TARGET_NAME} Network) +qt5_use_modules(${TARGET_NAME} Network Widgets) # include glm include(${MACRO_DIR}/IncludeGLM.cmake) @@ -30,6 +31,7 @@ link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particle-server ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(script-engine ${TARGET_NAME} ${ROOT_DIR}) #testing include_directories(${ROOT_DIR}/externals/civetweb/include) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 8f02ed9a2e..e8bffc8eb2 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -24,8 +24,6 @@ Agent::Agent(const unsigned char* dataBuffer, int numBytes) : ThreadedAssignment(dataBuffer, numBytes) { - _particleScriptingInterface.init(); - _voxelScriptingInterface.init(); } void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { @@ -34,12 +32,12 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& // PACKET_TYPE_JURISDICTION, first byte is the node type... switch (dataByteArray[headerBytes]) { case NODE_TYPE_VOXEL_SERVER: - _voxelScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr, + _scriptEngine.getVoxelScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); break; case NODE_TYPE_PARTICLE_SERVER: - _particleScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr, + _scriptEngine.getParticleScriptingInterface()->getJurisdictionListener()->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); break; @@ -76,43 +74,12 @@ void Agent::run() { loop.exec(); QString scriptContents(reply->readAll()); - QScriptEngine engine; - - // register meta-type for glm::vec3 conversions - registerMetaTypes(&engine); - - QScriptValue agentValue = engine.newQObject(this); - engine.globalObject().setProperty("Agent", agentValue); - - QScriptValue voxelScripterValue = engine.newQObject(&_voxelScriptingInterface); - engine.globalObject().setProperty("Voxels", voxelScripterValue); - - QScriptValue particleScripterValue = engine.newQObject(&_particleScriptingInterface); - engine.globalObject().setProperty("Particles", particleScripterValue); - - QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); - engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); - - const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; - - // let the VoxelPacketSender know how frequently we plan to call it - _voxelScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); - _particleScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); qDebug() << "Downloaded script:" << scriptContents << "\n"; - QScriptValue result = engine.evaluate(scriptContents); - qDebug() << "Evaluated script.\n"; - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; - } timeval startTime; gettimeofday(&startTime, NULL); - int thisFrame = 0; - QTimer* domainServerTimer = new QTimer(this); connect(domainServerTimer, SIGNAL(timeout()), this, SLOT(checkInWithDomainServerOrExit())); domainServerTimer->start(DOMAIN_SERVER_CHECK_IN_USECS / 1000); @@ -124,50 +91,7 @@ void Agent::run() { QTimer* pingNodesTimer = new QTimer(this); connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); - - while (!_isFinished) { - - int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow(); - if (usecToSleep > 0) { - usleep(usecToSleep); - } - - QCoreApplication::processEvents(); - - bool willSendVisualDataCallBack = false; - - if (_voxelScriptingInterface.getVoxelPacketSender()->voxelServersExist()) { - // allow the scripter's call back to setup visual data - willSendVisualDataCallBack = true; - - // release the queue of edit voxel messages. - _voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); - - // since we're in non-threaded mode, call process so that the packets are sent - _voxelScriptingInterface.getVoxelPacketSender()->process(); - } - if (_particleScriptingInterface.getParticlePacketSender()->serversExist()) { - // allow the scripter's call back to setup visual data - willSendVisualDataCallBack = true; - - // release the queue of edit voxel messages. - _particleScriptingInterface.getParticlePacketSender()->releaseQueuedMessages(); - - // since we're in non-threaded mode, call process so that the packets are sent - _particleScriptingInterface.getParticlePacketSender()->process(); - } - - if (willSendVisualDataCallBack) { - emit willSendVisualDataCallback(); - } - - - if (engine.hasUncaughtException()) { - int line = engine.uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; - } - - - } + _scriptEngine.setScriptContents(scriptContents); + _scriptEngine.run(); } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 1c2be08662..0bcd1af78e 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -33,8 +34,7 @@ signals: void willSendAudioDataCallback(); void willSendVisualDataCallback(); private: - VoxelScriptingInterface _voxelScriptingInterface; - ParticleScriptingInterface _particleScriptingInterface; + ScriptEngine _scriptEngine; }; #endif /* defined(__hifi__Agent__) */ diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ec955f5fb4..ee6caaaf9b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -220,12 +220,12 @@ void DomainServer::readAvailableDatagrams() { } } else if (packetData[0] == PACKET_TYPE_REQUEST_ASSIGNMENT) { - qDebug("Received a request for assignment.\n"); - if (_assignmentQueue.size() > 0) { // construct the requested assignment from the packet data Assignment requestAssignment(packetData, receivedBytes); + qDebug("Received a request for assignment type %i from %s.\n", requestAssignment.getType(), qPrintable(senderSockAddr.getAddress().toString())); + Assignment* assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); if (assignmentToDeploy) { @@ -243,6 +243,8 @@ void DomainServer::readAvailableDatagrams() { } } + } else { + qDebug("Received an invalid assignment request from %s.\n", qPrintable(senderSockAddr.getAddress().toString())); } } } diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c447bfa1a5..ecc6ff79cb 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -93,10 +93,11 @@ include(${MACRO_DIR}/LinkHifiLibrary.cmake) link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(metavoxels ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR}) -link_hifi_library(scriptengine ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(script-engine ${TARGET_NAME} ${ROOT_DIR}) # find required libraries find_package(Faceshift) @@ -135,7 +136,7 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) -qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Svg WebKit WebKitWidgets) +qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets) # include headers for interface and InterfaceConfig. include_directories( diff --git a/interface/resources/scripts/sphere.js b/interface/resources/scripts/sphere.js new file mode 100644 index 0000000000..403374e812 --- /dev/null +++ b/interface/resources/scripts/sphere.js @@ -0,0 +1,171 @@ +// +// sphere.js +// interface +// +// Created by Andrzej Kapolka on 12/17/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +function strictIndexOf(array, element) { + for (var i = 0; i < array.length; i++) { + if (array[i] == element) { + return i; + } + } + return -1; +} + +var colorIndex; +var normalIndex; +var visitor; +var info; + +var MAX_DEPTH = 4; + +var sphereCenter = [ 0.5, 0.5, 0.5 ]; +var sphereColor = 0xFFFF00FF; +var sphereRadius = 0.25; +var sphereRadiusSquared = sphereRadius * sphereRadius; + +function lengthSquared(x, y, z) { + return x*x + y*y + z*z; +} + +function setNormal(vector) { + if (normalIndex != -1) { + var length = Math.sqrt(lengthSquared(vector[0], vector[1], vector[2])); + if (length == 0.0) { + info.attributeValues[normalIndex] = 0x007F00; + + } else { + var scale = 127.0 / length; + info.attributeValues[normalIndex] = + (Math.floor(vector[0] * scale) & 0xFF) << 16 | + (Math.floor(vector[1] * scale) & 0xFF) << 8 | + Math.floor(vector[2] * scale) & 0xFF; + } + } +} + +function guide(minimum, size, depth) { + info.minimum = minimum; + info.size = size; + + // start with a relative fast bounding volume test to find most non-intersecting states + var maximum = [ minimum[0] + size, minimum[1] + size, minimum[2] + size ]; + if (minimum[0] >= sphereCenter[0] + sphereRadius || + minimum[1] >= sphereCenter[1] + sphereRadius || + minimum[2] >= sphereCenter[2] + sphereRadius || + maximum[0] <= sphereCenter[0] - sphereRadius || + maximum[1] <= sphereCenter[1] - sphereRadius || + maximum[2] <= sphereCenter[2] - sphereRadius) { + info.isLeaf = true; + if (colorIndex != -1) { + info.attributeValues[colorIndex] = 0x0; + } + visitor.visit(info); + return; + } + + var halfSize = size / 2; + var center = [ minimum[0] + halfSize, minimum[1] + halfSize, minimum[2] + halfSize ]; + var vector = [ center[0] - sphereCenter[0], center[1] - sphereCenter[1], center[2] - sphereCenter[2] ]; + + // count the number of points inside the sphere + var inside = 0; + if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - minimum[2]) <= + sphereRadiusSquared) { + inside++; + } + if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - minimum[2]) <= + sphereRadiusSquared) { + inside++; + } + if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - minimum[2]) <= + sphereRadiusSquared) { + inside++; + } + if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - minimum[2]) <= + sphereRadiusSquared) { + inside++; + } + if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - maximum[2]) <= + sphereRadiusSquared) { + inside++; + } + if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - maximum[2]) <= + sphereRadiusSquared) { + inside++; + } + if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - maximum[2]) <= + sphereRadiusSquared) { + inside++; + } + if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - maximum[2]) <= + sphereRadiusSquared) { + inside++; + } + + // see if all points are in the sphere + if (inside == 8) { + info.isLeaf = true; + if (colorIndex != -1) { + info.attributeValues[colorIndex] = sphereColor; + } + setNormal(vector); + visitor.visit(info); + return; + } + + // if we've reached max depth, compute alpha using a volume estimate + if (depth == MAX_DEPTH) { + info.isLeaf = true; + if (inside >= 3) { + if (colorIndex != -1) { + info.attributeValues[colorIndex] = sphereColor; + } + setNormal(vector); + + } else { + if (colorIndex != -1) { + info.attributeValues[colorIndex] = 0x0; + } + } + visitor.visit(info); + return; + } + + // recurse + info.isLeaf = false; + if (!visitor.visit(info)) { + return; + } + depth += 1; + guide(minimum, halfSize, depth); + guide([ center[0], minimum[1], minimum[2] ], halfSize, depth); + guide([ minimum[0], center[1], minimum[2] ], halfSize, depth); + guide([ center[0], center[1], minimum[2] ], halfSize, depth); + guide([ minimum[0], minimum[1], center[2] ], halfSize, depth); + guide([ center[0], minimum[1], center[2] ], halfSize, depth); + guide([ minimum[0], center[1], center[2] ], halfSize, depth); + guide([ center[0], center[1], center[2] ], halfSize, depth); +} + +(function(visitation) { + var attributes = visitation.visitor.getAttributes(); + colorIndex = strictIndexOf(attributes, AttributeRegistry.colorAttribute); + normalIndex = strictIndexOf(attributes, AttributeRegistry.normalAttribute); + visitor = visitation.visitor; + info = { attributeValues: new Array(attributes.length) }; + + // have the sphere orbit the center and pulse in size + var time = new Date().getTime(); + var ROTATE_PERIOD = 400.0; + sphereCenter[0] = 0.5 + 0.25 * Math.cos(time / ROTATE_PERIOD); + sphereCenter[2] = 0.5 + 0.25 * Math.sin(time / ROTATE_PERIOD); + var PULSE_PERIOD = 300.0; + sphereRadius = 0.25 + 0.0625 * Math.cos(time / PULSE_PERIOD); + sphereRadiusSquared = sphereRadius * sphereRadius; + + guide(visitation.info.minimum, visitation.info.size, 0); +}) diff --git a/interface/resources/shaders/metavoxel_point.vert b/interface/resources/shaders/metavoxel_point.vert new file mode 100644 index 0000000000..b0472088c0 --- /dev/null +++ b/interface/resources/shaders/metavoxel_point.vert @@ -0,0 +1,25 @@ +#version 120 + +// +// metavoxel_point.vert +// vertex shader +// +// Created by Andrzej Kapolka on 12/12/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +uniform float pointScale; + +void main(void) { + + // standard diffuse lighting + gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb + + gl_LightSource[0].diffuse.rgb * max(0.0, dot(gl_NormalMatrix * gl_Normal, gl_LightSource[0].position.xyz))), + gl_Color.a); + + // extract the first three components of the vertex for position + gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0); + + // the final component is the size in world space + gl_PointSize = pointScale * gl_Vertex.w / gl_Position.w; +} diff --git a/interface/resources/styles/Inconsolata.otf b/interface/resources/styles/Inconsolata.otf new file mode 100644 index 0000000000..348889828d Binary files /dev/null and b/interface/resources/styles/Inconsolata.otf differ diff --git a/interface/resources/styles/log_dialog.qss b/interface/resources/styles/log_dialog.qss new file mode 100644 index 0000000000..fe3675f682 --- /dev/null +++ b/interface/resources/styles/log_dialog.qss @@ -0,0 +1,7 @@ +QPlainTextEdit { + font-family: Inconsolata, Lucida Console, Andale Mono, Monaco; + font-size: 16px; + padding-left: 28px; + color: #333333; + background-color: #FFFFFF; +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5dff756eec..24cdabe772 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -143,11 +143,17 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _pasteMode(false) { _applicationStartupTime = startup_time; + + switchToResourcesParentIfRequired(); + QFontDatabase::addApplicationFont("resources/styles/Inconsolata.otf"); _window->setWindowTitle("Interface"); - - qDebug( "[VERSION] Build sequence: %i", BUILD_VERSION); - + qInstallMessageHandler(messageHandler); + + // call Menu getInstance static method to set up the menu + _window->setMenuBar(Menu::getInstance()); + + qDebug("[VERSION] Build sequence: %i", BUILD_VERSION); unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast(argv); @@ -173,16 +179,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // network receive thread and voxel parsing thread are both controlled by the --nonblocking command line _enableProcessVoxelsThread = _enableNetworkThread = !cmdOptionExists(argc, constArgv, "--nonblocking"); - - // setup QSettings -#ifdef Q_OS_MAC - QString resourcesPath = QCoreApplication::applicationDirPath() + "/../Resources"; -#else - QString resourcesPath = QCoreApplication::applicationDirPath() + "/resources"; -#endif - + // read the ApplicationInfo.ini file for Name/Version/Domain information - QSettings applicationInfo(resourcesPath + "/info/ApplicationInfo.ini", QSettings::IniFormat); + QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat); // set the associated application properties applicationInfo.beginGroup("INFO"); @@ -191,11 +190,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : setApplicationVersion(applicationInfo.value("version").toString()); setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); - + _settings = new QSettings(this); - - // call Menu getInstance static method to set up the menu - _window->setMenuBar(Menu::getInstance()); // Check to see if the user passed in a command line option for loading a local // Voxel File. @@ -251,6 +247,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : } Application::~Application() { + + qInstallMessageHandler(NULL); + // make sure we don't call the idle timer any more delete idleTimer; @@ -1858,6 +1857,8 @@ void Application::init() { _particles.init(); _particles.setViewFrustum(getViewFrustum()); + _metavoxels.init(); + _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_myAvatar); _palette.init(_glWidget->width(), _glWidget->height()); @@ -2376,6 +2377,15 @@ void Application::updateParticles(float deltaTime) { } } +void Application::updateMetavoxels(float deltaTime) { + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()"); + + if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) { + _metavoxels.simulate(deltaTime); + } +} + void Application::updateTransmitter(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateTransmitter()"); @@ -2521,6 +2531,7 @@ void Application::update(float deltaTime) { updateAvatars(deltaTime, mouseRayOrigin, mouseRayDirection); //loop through all the other avatars and simulate them... updateMyAvatarSimulation(deltaTime); // Simulate myself updateParticles(deltaTime); // Simulate particle cloud movements + updateMetavoxels(deltaTime); // update metavoxels updateTransmitter(deltaTime); // transmitter drive or pick updateCamera(deltaTime); // handle various camera tweaks like off axis projection updateDialogs(deltaTime); // update various stats dialogs if present @@ -3068,6 +3079,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } } + // also, metavoxels + if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::displaySide() ... metavoxels..."); + _metavoxels.render(); + } + // render particles... _particles.render(); @@ -4432,7 +4450,8 @@ void Application::loadScript() { bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself - ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance()); + ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(), + &_controllerScriptingInterface); scriptEngine->setupMenuItems(); // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so @@ -4460,3 +4479,12 @@ void Application::loadScript() { // restore the main window's active state _window->activateWindow(); } + +void Application::toggleLogDialog() { + if (! _logDialog) { + _logDialog = new LogDialog(_glWidget); + _logDialog->show(); + } else { + _logDialog->close(); + } +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 6a948f5b7a..e917f6d63d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -58,16 +59,20 @@ #include "renderer/AmbientOcclusionEffect.h" #include "renderer/GeometryCache.h" #include "renderer/GlowEffect.h" -#include "renderer/VoxelShader.h" +#include "renderer/MetavoxelSystem.h" #include "renderer/PointShader.h" #include "renderer/TextureCache.h" +#include "renderer/VoxelShader.h" #include "ui/BandwidthDialog.h" #include "ui/ChatEntry.h" #include "ui/VoxelStatsDialog.h" #include "ui/RearMirrorTools.h" #include "ui/LodToolsDialog.h" +#include "ui/LogDialog.h" #include "ParticleTreeRenderer.h" #include "ParticleEditHandle.h" +#include "ControllerScriptingInterface.h" + class QAction; class QActionGroup; @@ -221,7 +226,7 @@ public slots: void decreaseVoxelSize(); void increaseVoxelSize(); void loadScript(); - + void toggleLogDialog(); private slots: @@ -282,6 +287,7 @@ private: void updateThreads(float deltaTime); void updateMyAvatarSimulation(float deltaTime); void updateParticles(float deltaTime); + void updateMetavoxels(float deltaTime); void updateTransmitter(float deltaTime); void updateCamera(float deltaTime); void updateDialogs(float deltaTime); @@ -362,6 +368,8 @@ private: QByteArray _voxelsFilename; bool _wantToKillLocalVoxels; + MetavoxelSystem _metavoxels; + ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. Oscilloscope _audioScope; @@ -499,6 +507,8 @@ private: std::vector _voxelFades; std::vector _avatarFades; + ControllerScriptingInterface _controllerScriptingInterface; + QPointer _logDialog; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 25a5f01d06..400ff534df 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -174,9 +174,8 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples, if (sourceAudioFormat == destinationAudioFormat) { memcpy(destinationSamples, sourceSamples, numSourceSamples * sizeof(int16_t)); } else { - int destinationChannels = (destinationAudioFormat.channelCount() >= 2) ? 2 : destinationAudioFormat.channelCount(); float sourceToDestinationFactor = (sourceAudioFormat.sampleRate() / (float) destinationAudioFormat.sampleRate()) - * (sourceAudioFormat.channelCount() / (float) destinationChannels); + * (sourceAudioFormat.channelCount() / (float) destinationAudioFormat.channelCount()); // take into account the number of channels in source and destination // accomodate for the case where have an output with > 2 channels @@ -203,14 +202,15 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples, // upsample from 24 to 48 // for now this only supports a stereo to stereo conversion - this is our case for network audio to output int sourceIndex = 0; - int destinationToSourceFactor = (1 / sourceToDestinationFactor); int dtsSampleRateFactor = (destinationAudioFormat.sampleRate() / sourceAudioFormat.sampleRate()); + int sampleShift = destinationAudioFormat.channelCount() * dtsSampleRateFactor; + int destinationToSourceFactor = (1 / sourceToDestinationFactor); - for (int i = 0; i < numDestinationSamples; i += destinationAudioFormat.channelCount() * dtsSampleRateFactor) { + for (int i = 0; i < numDestinationSamples; i += sampleShift) { sourceIndex = (i / destinationToSourceFactor); // fill the L/R channels and make the rest silent - for (int j = i; j < i + (dtsSampleRateFactor * destinationAudioFormat.channelCount()); j++) { + for (int j = i; j < i + sampleShift; j++) { if (j % destinationAudioFormat.channelCount() == 0) { // left channel destinationSamples[j] = sourceSamples[sourceIndex]; diff --git a/interface/src/ControllerScriptingInterface.cpp b/interface/src/ControllerScriptingInterface.cpp new file mode 100644 index 0000000000..fd27eb2428 --- /dev/null +++ b/interface/src/ControllerScriptingInterface.cpp @@ -0,0 +1,183 @@ +// +// ControllerScriptingInterface.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/17/13 +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#include +#include "Application.h" +#include "ControllerScriptingInterface.h" + +const PalmData* ControllerScriptingInterface::getPrimaryPalm() const { + int leftPalmIndex, rightPalmIndex; + + const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); + handData->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); + + if (rightPalmIndex != -1) { + return &handData->getPalms()[rightPalmIndex]; + } + + return NULL; +} + +int ControllerScriptingInterface::getNumberOfActivePalms() const { + const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); + int numberOfPalms = handData->getNumPalms(); + int numberOfActivePalms = 0; + for (int i = 0; i < numberOfPalms; i++) { + if (getPalm(i)->isActive()) { + numberOfActivePalms++; + } + } + return numberOfActivePalms; +} + +const PalmData* ControllerScriptingInterface::getPalm(int palmIndex) const { + const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); + return &handData->getPalms()[palmIndex]; +} + +const PalmData* ControllerScriptingInterface::getActivePalm(int palmIndex) const { + const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); + int numberOfPalms = handData->getNumPalms(); + int numberOfActivePalms = 0; + for (int i = 0; i < numberOfPalms; i++) { + if (getPalm(i)->isActive()) { + if (numberOfActivePalms == palmIndex) { + return &handData->getPalms()[i]; + } + numberOfActivePalms++; + } + } + return NULL; +} + +bool ControllerScriptingInterface::isPrimaryButtonPressed() const { + const PalmData* primaryPalm = getPrimaryPalm(); + if (primaryPalm) { + if (primaryPalm->getControllerButtons() & BUTTON_FWD) { + return true; + } + } + + return false; +} + +glm::vec2 ControllerScriptingInterface::getPrimaryJoystickPosition() const { + const PalmData* primaryPalm = getPrimaryPalm(); + if (primaryPalm) { + return glm::vec2(primaryPalm->getJoystickX(), primaryPalm->getJoystickY()); + } + + return glm::vec2(0); +} + +int ControllerScriptingInterface::getNumberOfButtons() const { + return getNumberOfActivePalms() * NUMBER_OF_BUTTONS_PER_PALM; +} + +bool ControllerScriptingInterface::isButtonPressed(int buttonIndex) const { + int palmIndex = buttonIndex / NUMBER_OF_BUTTONS_PER_PALM; + int buttonOnPalm = buttonIndex % NUMBER_OF_BUTTONS_PER_PALM; + const PalmData* palmData = getActivePalm(palmIndex); + if (palmData) { + switch (buttonOnPalm) { + case 0: + return palmData->getControllerButtons() & BUTTON_0; + case 1: + return palmData->getControllerButtons() & BUTTON_1; + case 2: + return palmData->getControllerButtons() & BUTTON_2; + case 3: + return palmData->getControllerButtons() & BUTTON_3; + case 4: + return palmData->getControllerButtons() & BUTTON_4; + case 5: + return palmData->getControllerButtons() & BUTTON_FWD; + } + } + return false; +} + +int ControllerScriptingInterface::getNumberOfTriggers() const { + return getNumberOfActivePalms() * NUMBER_OF_TRIGGERS_PER_PALM; +} + +float ControllerScriptingInterface::getTriggerValue(int triggerIndex) const { + // we know there's one trigger per palm, so the triggerIndex is the palm Index + int palmIndex = triggerIndex; + const PalmData* palmData = getActivePalm(palmIndex); + if (palmData) { + return palmData->getTrigger(); + } + return 0.0f; +} + +int ControllerScriptingInterface::getNumberOfJoysticks() const { + return getNumberOfActivePalms() * NUMBER_OF_JOYSTICKS_PER_PALM; +} + +glm::vec2 ControllerScriptingInterface::getJoystickPosition(int joystickIndex) const { + // we know there's one joystick per palm, so the joystickIndex is the palm Index + int palmIndex = joystickIndex; + const PalmData* palmData = getActivePalm(palmIndex); + if (palmData) { + return glm::vec2(palmData->getJoystickX(), palmData->getJoystickY()); + } + return glm::vec2(0); +} + +int ControllerScriptingInterface::getNumberOfSpatialControls() const { + return getNumberOfActivePalms() * NUMBER_OF_SPATIALCONTROLS_PER_PALM; +} + +glm::vec3 ControllerScriptingInterface::getSpatialControlPosition(int controlIndex) const { + int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; + int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; + const PalmData* palmData = getActivePalm(palmIndex); + if (palmData) { + switch (controlOfPalm) { + case PALM_SPATIALCONTROL: + return palmData->getPosition(); + case TIP_SPATIALCONTROL: + return palmData->getTipPosition(); + } + } + return glm::vec3(0); // bad index +} + +glm::vec3 ControllerScriptingInterface::getSpatialControlVelocity(int controlIndex) const { + int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; + int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; + const PalmData* palmData = getActivePalm(palmIndex); + if (palmData) { + switch (controlOfPalm) { + case PALM_SPATIALCONTROL: + return palmData->getVelocity(); + case TIP_SPATIALCONTROL: + return palmData->getTipVelocity(); + } + } + return glm::vec3(0); // bad index +} + +glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex) const { + int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; + int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; + const PalmData* palmData = getActivePalm(palmIndex); + if (palmData) { + switch (controlOfPalm) { + case PALM_SPATIALCONTROL: + return palmData->getNormal(); + case TIP_SPATIALCONTROL: + return palmData->getNormal(); // currently the tip doesn't have a unique normal, use the palm normal + } + } + return glm::vec3(0); // bad index +} + + + diff --git a/interface/src/ControllerScriptingInterface.h b/interface/src/ControllerScriptingInterface.h new file mode 100644 index 0000000000..d0e032d52f --- /dev/null +++ b/interface/src/ControllerScriptingInterface.h @@ -0,0 +1,52 @@ +// +// ControllerScriptingInterface.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/17/13 +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__ControllerScriptingInterface__ +#define __hifi__ControllerScriptingInterface__ + +#include + +#include + +/// handles scripting of input controller commands from JS +class ControllerScriptingInterface : public AbstractControllerScriptingInterface { + Q_OBJECT + +public slots: + virtual bool isPrimaryButtonPressed() const; + virtual glm::vec2 getPrimaryJoystickPosition() const; + + virtual int getNumberOfButtons() const; + virtual bool isButtonPressed(int buttonIndex) const; + + virtual int getNumberOfTriggers() const; + virtual float getTriggerValue(int triggerIndex) const; + + virtual int getNumberOfJoysticks() const; + virtual glm::vec2 getJoystickPosition(int joystickIndex) const; + + virtual int getNumberOfSpatialControls() const; + virtual glm::vec3 getSpatialControlPosition(int controlIndex) const; + virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const; + virtual glm::vec3 getSpatialControlNormal(int controlIndex) const; + +private: + const PalmData* getPrimaryPalm() const; + const PalmData* getPalm(int palmIndex) const; + int getNumberOfActivePalms() const; + const PalmData* getActivePalm(int palmIndex) const; +}; + +const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip +const int NUMBER_OF_JOYSTICKS_PER_PALM = 1; +const int NUMBER_OF_TRIGGERS_PER_PALM = 1; +const int NUMBER_OF_BUTTONS_PER_PALM = 6; +const int PALM_SPATIALCONTROL = 0; +const int TIP_SPATIALCONTROL = 1; + +#endif /* defined(__hifi__ControllerScriptingInterface__) */ diff --git a/interface/src/LogDisplay.cpp b/interface/src/LogDisplay.cpp index 247ea4beb5..dc365a9ea0 100644 --- a/interface/src/LogDisplay.cpp +++ b/interface/src/LogDisplay.cpp @@ -6,12 +6,13 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -#include "LogDisplay.h" - #include #include #include +#include + +#include "LogDisplay.h" #include "Util.h" using namespace std; @@ -91,6 +92,7 @@ void LogDisplay::setCharacterSize(unsigned width, unsigned height) { void LogDisplay::addMessage(const char* ptr) { pthread_mutex_lock(& _mutex); + emit logReceived(ptr); // T-pipe, if requested if (_stream != 0l) { @@ -118,7 +120,7 @@ void LogDisplay::addMessage(const char* ptr) { _writePos = _chars; } - if (++_writtenInLine >= _lineLength || c == '\0') { + if (c == '\0') { // new line? store its start to the line buffer and mark next line as empty ++_lastLinePos; @@ -148,13 +150,24 @@ void LogDisplay::addMessage(const char* ptr) { // remember start position in character buffer for next line and reset character count _writeLineStartPos = _writePos; - _writtenInLine = 0; } } pthread_mutex_unlock(& _mutex); } +QStringList LogDisplay::getLogData() { + // wait for adding new log data whilr iterating over _lines + pthread_mutex_lock(& _mutex); + QStringList list; + int i = 0; + while (_lines[i] != *_lastLinePos) { + list.append(_lines[i++]); + } + pthread_mutex_unlock(& _mutex); + return list; +} + // // Rendering // diff --git a/interface/src/LogDisplay.h b/interface/src/LogDisplay.h index 6f90df3724..285325b180 100644 --- a/interface/src/LogDisplay.h +++ b/interface/src/LogDisplay.h @@ -14,7 +14,8 @@ #include "ui/TextRenderer.h" -class LogDisplay { +class LogDisplay : public QObject { + Q_OBJECT public: static LogDisplay instance; @@ -43,6 +44,11 @@ public: static unsigned const LINE_BUFFER_SIZE = 256; // number of lines that are buffered static unsigned const MAX_MESSAGE_LENGTH = 512; // maximum number of characters for a message + QStringList getLogData(); + +signals: + void logReceived(QString message); + private: // use static 'instance' to access the single instance LogDisplay(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d81e22d818..98b807fea2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -265,7 +265,7 @@ Menu::Menu() : addDisabledActionAndSeparator(viewMenu, "Stats"); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L); + addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); @@ -285,7 +285,8 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ParticleCloud, 0, false); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false); - + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, false); + QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options"); @@ -701,7 +702,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, action = destinationMenu->addAction(actionName); action->setShortcut(shortcut); } - action->setMenuRole(role); + action->setMenuRole(role); _actionHash.insert(actionName, action); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b62f49abe4..73bb0472b4 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -146,7 +146,6 @@ private: int _boundaryLevelAdjust; QAction* _useVoxelShader; int _maxVoxelPacketsPerSecond; - QMenu* _activeScriptsMenu; }; @@ -219,6 +218,7 @@ namespace MenuOption { const QString Login = "Login"; const QString LookAtIndicator = "Look-at Indicator"; const QString LookAtVectors = "Look-at Vectors"; + const QString Metavoxels = "Metavoxels"; const QString Mirror = "Mirror"; const QString MoveWithLean = "Move with Lean"; const QString NewVoxelCullingMode = "New Voxel Culling Mode"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f4a09ad069..7762f5afbc 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -338,8 +338,6 @@ void Avatar::render(bool forceRenderHead) { } } - - // returns true if the Leap controls any of the avatar's hands. bool Avatar::updateLeapHandPositions() { bool returnValue = false; @@ -443,8 +441,6 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { return glm::angleAxis(angle * proportion, axis); } - - void Avatar::renderBody(bool forceRenderHead) { if (_head.getVideoFace().isFullFrame()) { diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index bd3a0a1fac..2fa59ee43f 100755 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -24,11 +24,11 @@ using namespace std; const float FINGERTIP_VOXEL_SIZE = 0.05; const int TOY_BALL_HAND = 1; const float TOY_BALL_RADIUS = 0.05f; -const float TOY_BALL_DAMPING = 0.99f; +const float TOY_BALL_DAMPING = 0.1f; const glm::vec3 NO_VELOCITY = glm::vec3(0,0,0); const glm::vec3 NO_GRAVITY = glm::vec3(0,0,0); const float NO_DAMPING = 0.f; -const glm::vec3 TOY_BALL_GRAVITY = glm::vec3(0,-0.5,0); +const glm::vec3 TOY_BALL_GRAVITY = glm::vec3(0,-2.0,0); const QString TOY_BALL_UPDATE_SCRIPT(""); const float PALM_COLLISION_RADIUS = 0.03f; const float CATCH_RADIUS = 0.2f; @@ -98,7 +98,7 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f // update the particle with it's new state... #ifdef DEBUG_HAND - qDebug("Update caught particle!\n"); + qDebug("Caught!\n"); #endif caughtParticle->updateParticle(newPosition, closestParticle->getRadius(), @@ -108,6 +108,7 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f NO_DAMPING, IN_HAND, // we just grabbed it! closestParticle->getUpdateScript()); + // now tell our hand about us having caught it... _toyBallInHand[handID] = true; @@ -115,6 +116,11 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f //printf(">>>>>>> caught... handID:%d particle ID:%d _toyBallInHand[handID] = true\n", handID, closestParticle->getID()); _ballParticleEditHandles[handID] = caughtParticle; caughtParticle = NULL; + // Play a catch sound! + Application::getInstance()->getAudio()->startDrumSound(1.0, + 300, + 0.5, + 0.05); } } @@ -158,11 +164,17 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f TOY_BALL_DAMPING, IN_HAND, TOY_BALL_UPDATE_SCRIPT); + // Play a new ball sound + Application::getInstance()->getAudio()->startDrumSound(1.0, + 2000, + 0.5, + 0.02); + } } else { // Ball is in hand #ifdef DEBUG_HAND - qDebug("Ball in hand\n"); + //qDebug("Ball in hand\n"); #endif glm::vec3 ballPosition = ballFromHand ? palm.getPosition() : fingerTipPosition; _ballParticleEditHandles[handID]->updateParticle(ballPosition / (float)TREE_SCALE, @@ -178,13 +190,14 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f // If toy ball just released, add velocity to it! if (_toyBallInHand[handID]) { + const float THROWN_VELOCITY_SCALING = 1.5f; _toyBallInHand[handID] = false; glm::vec3 ballPosition = ballFromHand ? palm.getPosition() : fingerTipPosition; glm::vec3 ballVelocity = ballFromHand ? palm.getRawVelocity() : palm.getTipVelocity(); glm::quat avatarRotation = _owningAvatar->getOrientation(); ballVelocity = avatarRotation * ballVelocity; + ballVelocity *= THROWN_VELOCITY_SCALING; - // ball is no longer in hand... #ifdef DEBUG_HAND qDebug("Threw ball, v = %.3f\n", glm::length(ballVelocity)); #endif @@ -201,6 +214,13 @@ void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, f // note: deleting the edit handle doesn't effect the actual particle delete _ballParticleEditHandles[handID]; _ballParticleEditHandles[handID] = NULL; + + // Play a throw sound + Application::getInstance()->getAudio()->startDrumSound(1.0, + 3000, + 0.5, + 0.02); + } } diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 07788fefe0..7a5703a881 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -19,11 +19,23 @@ #include #include +#include int main(int argc, const char * argv[]) { timeval startup_time; gettimeofday(&startup_time, NULL); + // Debug option to demonstrate that the client's local time does not + // need to be in sync with any other network node. This forces clock + // skew for the individual client + const char* CLOCK_SKEW = "--clockSkew"; + const char* clockSkewOption = getCmdOption(argc, argv, CLOCK_SKEW); + if (clockSkewOption) { + int clockSkew = atoi(clockSkewOption); + usecTimestampNowForceClockSkew(clockSkew); + qDebug("clockSkewOption=%s clockSkew=%d\n", clockSkewOption, clockSkew); + } + int exitCode; { Application app(argc, const_cast(argv), startup_time); diff --git a/interface/src/renderer/MetavoxelSystem.cpp b/interface/src/renderer/MetavoxelSystem.cpp new file mode 100644 index 0000000000..543a8b6301 --- /dev/null +++ b/interface/src/renderer/MetavoxelSystem.cpp @@ -0,0 +1,122 @@ +// +// MetavoxelSystem.cpp +// interface +// +// Created by Andrzej Kapolka on 12/10/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include +#include + +#include + +#include "Application.h" +#include "MetavoxelSystem.h" + +ProgramObject MetavoxelSystem::_program; +int MetavoxelSystem::_pointScaleLocation; + +MetavoxelSystem::MetavoxelSystem() : + _pointVisitor(_points), + _buffer(QOpenGLBuffer::VertexBuffer) { +} + +void MetavoxelSystem::init() { + if (!_program.isLinked()) { + switchToResourcesParentIfRequired(); + _program.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/metavoxel_point.vert"); + _program.link(); + + _pointScaleLocation = _program.uniformLocation("pointScale"); + } + + AttributeRegistry::getInstance()->configureScriptEngine(&_scriptEngine); + + QFile scriptFile("resources/scripts/sphere.js"); + scriptFile.open(QIODevice::ReadOnly); + QScriptValue guideFunction = _scriptEngine.evaluate(QTextStream(&scriptFile).readAll()); + _data.setAttributeValue(MetavoxelPath(), AttributeValue(AttributeRegistry::getInstance()->getGuideAttribute(), + encodeInline(PolymorphicDataPointer(new ScriptedMetavoxelGuide(guideFunction))))); + + _buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); + _buffer.create(); +} + +void MetavoxelSystem::simulate(float deltaTime) { + _points.clear(); + _data.guide(_pointVisitor); + + _buffer.bind(); + int bytes = _points.size() * sizeof(Point); + if (_buffer.size() < bytes) { + _buffer.allocate(_points.constData(), bytes); + } else { + _buffer.write(0, _points.constData(), bytes); + } + _buffer.release(); +} + +void MetavoxelSystem::render() { + int viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + const int VIEWPORT_WIDTH_INDEX = 2; + const int VIEWPORT_HEIGHT_INDEX = 3; + float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX]; + float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX]; + float viewportDiagonal = sqrtf(viewportWidth*viewportWidth + viewportHeight*viewportHeight); + float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(), + Application::getInstance()->getViewFrustum()->getNearTopRight()); + + _program.bind(); + _program.setUniformValue(_pointScaleLocation, viewportDiagonal * + Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal); + + _buffer.bind(); + + Point* pt = 0; + glVertexPointer(4, GL_FLOAT, sizeof(Point), &pt->vertex); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Point), &pt->color); + glNormalPointer(GL_BYTE, sizeof(Point), &pt->normal); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); + + glDrawArrays(GL_POINTS, 0, _points.size()); + + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + + _buffer.release(); + + _program.release(); +} + +MetavoxelSystem::PointVisitor::PointVisitor(QVector& points) : + MetavoxelVisitor(QVector() << + AttributeRegistry::getInstance()->getColorAttribute() << + AttributeRegistry::getInstance()->getNormalAttribute()), + _points(points) { +} + +bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) { + if (!info.isLeaf) { + return true; + } + QRgb color = info.attributeValues.at(0).getInlineValue(); + QRgb normal = info.attributeValues.at(1).getInlineValue(); + int alpha = qAlpha(color); + if (alpha > 0) { + Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), + { qRed(color), qGreen(color), qBlue(color), alpha }, { qRed(normal), qGreen(normal), qBlue(normal) } }; + _points.append(point); + } + return false; +} diff --git a/interface/src/renderer/MetavoxelSystem.h b/interface/src/renderer/MetavoxelSystem.h new file mode 100644 index 0000000000..b8617d99a4 --- /dev/null +++ b/interface/src/renderer/MetavoxelSystem.h @@ -0,0 +1,61 @@ +// +// MetavoxelSystem.h +// interface +// +// Created by Andrzej Kapolka on 12/10/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelSystem__ +#define __interface__MetavoxelSystem__ + +#include +#include +#include + +#include + +#include + +#include "ProgramObject.h" + +/// Renders a metavoxel tree. +class MetavoxelSystem { +public: + + MetavoxelSystem(); + + void init(); + + void simulate(float deltaTime); + void render(); + +private: + + class Point { + public: + glm::vec4 vertex; + quint8 color[4]; + quint8 normal[3]; + }; + + class PointVisitor : public MetavoxelVisitor { + public: + PointVisitor(QVector& points); + virtual bool visit(const MetavoxelInfo& info); + + private: + QVector& _points; + }; + + static ProgramObject _program; + static int _pointScaleLocation; + + QScriptEngine _scriptEngine; + MetavoxelData _data; + QVector _points; + PointVisitor _pointVisitor; + QOpenGLBuffer _buffer; +}; + +#endif /* defined(__interface__MetavoxelSystem__) */ diff --git a/interface/src/ui/LogDialog.cpp b/interface/src/ui/LogDialog.cpp new file mode 100644 index 0000000000..013c60d993 --- /dev/null +++ b/interface/src/ui/LogDialog.cpp @@ -0,0 +1,75 @@ +// +// LogDialog.cpp +// interface +// +// Created by Stojce Slavkovski on 12/12/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include +#include + +#include "SharedUtil.h" +#include "ui/LogDialog.h" +#include "LogDisplay.h" + +const int INITIAL_WIDTH = 720; +const float INITIAL_HEIGHT_RATIO = 0.6f; + +int cursorMeta = qRegisterMetaType("QTextCursor"); +int blockMeta = qRegisterMetaType("QTextBlock"); + +LogDialog::LogDialog(QWidget* parent) : QDialog(parent, Qt::Dialog) { + + setWindowTitle("Log"); + + _logTextBox = new QPlainTextEdit(this); + _logTextBox->setReadOnly(true); + _logTextBox->show(); + + switchToResourcesParentIfRequired(); + QFile styleSheet("resources/styles/log_dialog.qss"); + + if (styleSheet.open(QIODevice::ReadOnly)) { + setStyleSheet(styleSheet.readAll()); + } + + QDesktopWidget desktop; + QRect screen = desktop.screenGeometry(); + resize(INITIAL_WIDTH, static_cast(screen.height() * INITIAL_HEIGHT_RATIO)); + move(screen.center() - rect().center()); + + setAttribute(Qt::WA_DeleteOnClose); +} + +LogDialog::~LogDialog() { + deleteLater(); +} + +void LogDialog::showEvent(QShowEvent *e) { + _logTextBox->clear(); + + pthread_mutex_lock(& _mutex); + QStringList _logData = LogDisplay::instance.getLogData(); + + connect(&LogDisplay::instance, &LogDisplay::logReceived, this, &LogDialog::appendLogLine); + for(int i = 0; i < _logData.size(); ++i) { + appendLogLine(_logData[i]); + } + + pthread_mutex_unlock(& _mutex); +} + +void LogDialog::resizeEvent(QResizeEvent *e) { + _logTextBox->resize(width(), height()); +} + +void LogDialog::appendLogLine(QString logLine) { + if (isVisible()) { + pthread_mutex_lock(& _mutex); + _logTextBox->appendPlainText(logLine.simplified()); + pthread_mutex_unlock(& _mutex); + _logTextBox->ensureCursorVisible(); + } +} diff --git a/interface/src/ui/LogDialog.h b/interface/src/ui/LogDialog.h new file mode 100644 index 0000000000..808ac4a485 --- /dev/null +++ b/interface/src/ui/LogDialog.h @@ -0,0 +1,36 @@ +// +// LogDialog.h +// interface +// +// Created by Stojce Slavkovski on 12/12/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__LogDialog__ +#define __interface__LogDialog__ + +#include +#include + +class LogDialog : public QDialog { + Q_OBJECT + +public: + LogDialog(QWidget* parent); + ~LogDialog(); + +public slots: + void appendLogLine(QString logLine); + +protected: + void resizeEvent(QResizeEvent* e); + void showEvent(QShowEvent* e); + +private: + QPlainTextEdit* _logTextBox; + pthread_mutex_t _mutex; + +}; + +#endif + diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index cf91af902a..e8b2c97ff0 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -169,6 +169,9 @@ int HandData::decodeRemoteData(unsigned char* sourceBuffer) { palm.setRawNormal(handNormal); palm.setActive(true); + // For received data, set the sixense controller ID to match the order initialized and sent - 0 Left, 1 Right + palm.setSixenseID(handIndex); + for (unsigned int fingerIndex = 0; fingerIndex < numFingers; ++fingerIndex) { if (fingerIndex < palm.getNumFingers()) { FingerData& finger = palm.getFingers()[fingerIndex]; diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index abeb7f9686..a0c8fed980 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -53,9 +53,10 @@ public: glm::vec3 worldPositionToLeapPosition(const glm::vec3& worldPosition) const; glm::vec3 worldVectorToLeapVector(const glm::vec3& worldVector) const; - std::vector& getPalms() { return _palms; } - size_t getNumPalms() { return _palms.size(); } - PalmData& addNewPalm(); + std::vector& getPalms() { return _palms; } + const std::vector& getPalms() const { return _palms; } + size_t getNumPalms() const { return _palms.size(); } + PalmData& addNewPalm(); /// Finds the indices of the left and right palms according to their locations, or -1 if either or /// both is not found. @@ -137,17 +138,18 @@ public: const glm::vec3& getRawPosition() const { return _rawPosition; } const glm::vec3& getRawNormal() const { return _rawNormal; } - bool isActive() const { return _isActive; } - int getLeapID() const { return _leapID; } - int getSixenseID() const { return _sixenseID; } + bool isActive() const { return _isActive; } + int getLeapID() const { return _leapID; } + int getSixenseID() const { return _sixenseID; } - std::vector& getFingers() { return _fingers; } - size_t getNumFingers() { return _fingers.size(); } + std::vector& getFingers() { return _fingers; } + const std::vector& getFingers() const { return _fingers; } + size_t getNumFingers() const { return _fingers.size(); } - void setActive(bool active) { _isActive = active; } - void setLeapID(int id) { _leapID = id; } - void setSixenseID(int id) { _sixenseID = id; } + void setActive(bool active) { _isActive = active; } + void setLeapID(int id) { _leapID = id; } + void setSixenseID(int id) { _sixenseID = id; } void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; }; glm::quat getRawRotation() const { return _rawRotation; } @@ -164,27 +166,27 @@ public: const glm::vec3& getTipVelocity() const { return _tipVelocity; } void setTipVelocity(const glm::vec3& velocity) { _tipVelocity = velocity; } - void incrementFramesWithoutData() { _numFramesWithoutData++; } - void resetFramesWithoutData() { _numFramesWithoutData = 0; } - int getFramesWithoutData() const { return _numFramesWithoutData; } + void incrementFramesWithoutData() { _numFramesWithoutData++; } + void resetFramesWithoutData() { _numFramesWithoutData = 0; } + int getFramesWithoutData() const { return _numFramesWithoutData; } // Controller buttons void setControllerButtons(int controllerButtons) { _controllerButtons = controllerButtons; } void setLastControllerButtons(int controllerButtons) { _lastControllerButtons = controllerButtons; } - int getControllerButtons() { return _controllerButtons; } - int getLastControllerButtons() { return _lastControllerButtons; } + int getControllerButtons() const { return _controllerButtons; } + int getLastControllerButtons() const { return _lastControllerButtons; } void setTrigger(float trigger) { _trigger = trigger; } - float getTrigger() { return _trigger; } + float getTrigger() const { return _trigger; } void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; } - float getJoystickX() { return _joystickX; } - float getJoystickY() { return _joystickY; } + float getJoystickX() const { return _joystickX; } + float getJoystickY() const { return _joystickY; } - bool getIsCollidingWithVoxel() { return _isCollidingWithVoxel; } + bool getIsCollidingWithVoxel() const { return _isCollidingWithVoxel; } void setIsCollidingWithVoxel(bool isCollidingWithVoxel) { _isCollidingWithVoxel = isCollidingWithVoxel; } - bool getIsCollidingWithPalm() { return _isCollidingWithPalm; } + bool getIsCollidingWithPalm() const { return _isCollidingWithPalm; } void setIsCollidingWithPalm(bool isCollidingWithPalm) { _isCollidingWithPalm = isCollidingWithPalm; } private: diff --git a/libraries/metavoxels/CMakeLists.txt b/libraries/metavoxels/CMakeLists.txt new file mode 100644 index 0000000000..0f9c1c695c --- /dev/null +++ b/libraries/metavoxels/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) + +set(ROOT_DIR ../..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +# setup for find modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") + +set(TARGET_NAME metavoxels) + +find_package(Qt5Widgets REQUIRED) + +include(${MACRO_DIR}/SetupHifiLibrary.cmake) +setup_hifi_library(${TARGET_NAME}) + +qt5_use_modules(${TARGET_NAME} Widgets Script) + +include(${MACRO_DIR}/IncludeGLM.cmake) +include_glm(${TARGET_NAME} ${ROOT_DIR}) + diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp new file mode 100644 index 0000000000..5fb03035da --- /dev/null +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -0,0 +1,143 @@ +// +// AttributeRegistry.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 12/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include "AttributeRegistry.h" +#include "MetavoxelData.h" + +AttributeRegistry AttributeRegistry::_instance; + +AttributeRegistry::AttributeRegistry() : + _guideAttribute(registerAttribute(new PolymorphicAttribute("guide", PolymorphicDataPointer(new DefaultMetavoxelGuide())))), + _colorAttribute(registerAttribute(new QRgbAttribute("color"))), + _normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) { +} + +void AttributeRegistry::configureScriptEngine(QScriptEngine* engine) { + QScriptValue registry = engine->newObject(); + registry.setProperty("colorAttribute", engine->newQObject(_colorAttribute.data())); + registry.setProperty("normalAttribute", engine->newQObject(_normalAttribute.data())); + registry.setProperty("getAttribute", engine->newFunction(getAttribute, 1)); + engine->globalObject().setProperty("AttributeRegistry", registry); +} + +AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute) { + AttributePointer& pointer = _attributes[attribute->getName()]; + if (!pointer) { + pointer = attribute; + } + return pointer; +} + +QScriptValue AttributeRegistry::getAttribute(QScriptContext* context, QScriptEngine* engine) { + return engine->newQObject(_instance.getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership, + QScriptEngine::PreferExistingWrapperObject); +} + +AttributeValue::AttributeValue(const AttributePointer& attribute) : + _attribute(attribute), _value(attribute ? attribute->getDefaultValue() : NULL) { +} + +AttributeValue::AttributeValue(const AttributePointer& attribute, void* value) : + _attribute(attribute), _value(value) { +} + +void* AttributeValue::copy() const { + return _attribute->create(_value); +} + +bool AttributeValue::isDefault() const { + return !_attribute || _attribute->equal(_value, _attribute->getDefaultValue()); +} + +bool AttributeValue::operator==(const AttributeValue& other) const { + return _attribute == other._attribute && (!_attribute || _attribute->equal(_value, other._value)); +} + +bool AttributeValue::operator==(void* other) const { + return _attribute && _attribute->equal(_value, other); +} + +OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) : + AttributeValue(attribute, attribute ? attribute->create() : NULL) { +} + +OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) : + AttributeValue(attribute, attribute ? attribute->create(value) : NULL) { +} + +OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) : + AttributeValue(other.getAttribute(), other.getAttribute() ? other.copy() : NULL) { +} + +OwnedAttributeValue::~OwnedAttributeValue() { + if (_attribute) { + _attribute->destroy(_value); + } +} + +OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) { + if (_attribute) { + _attribute->destroy(_value); + } + if ((_attribute = other.getAttribute())) { + _value = _attribute->create(other.getValue()); + } + return *this; +} + +Attribute::Attribute(const QString& name) { + setObjectName(name); +} + +Attribute::~Attribute() { +} + +QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) : + InlineAttribute(name, defaultValue) { +} + +bool QRgbAttribute::merge(void*& parent, void* children[]) const { + QRgb firstValue = decodeInline(children[0]); + int totalRed = qRed(firstValue); + int totalGreen = qGreen(firstValue); + int totalBlue = qBlue(firstValue); + int totalAlpha = qAlpha(firstValue); + bool allChildrenEqual = true; + for (int i = 1; i < Attribute::MERGE_COUNT; i++) { + QRgb value = decodeInline(children[i]); + totalRed += qRed(value); + totalGreen += qGreen(value); + totalBlue += qBlue(value); + totalAlpha += qAlpha(value); + allChildrenEqual &= (firstValue == value); + } + parent = encodeInline(qRgba(totalRed / MERGE_COUNT, totalGreen / MERGE_COUNT, + totalBlue / MERGE_COUNT, totalAlpha / MERGE_COUNT)); + return allChildrenEqual; +} + +void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* engine) const { + return encodeInline((QRgb)value.toUInt32()); +} + +PolymorphicData::~PolymorphicData() { +} + +template<> PolymorphicData* QExplicitlySharedDataPointer::clone() { + return d->clone(); +} + +PolymorphicAttribute::PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue) : + InlineAttribute(name, defaultValue) { +} + +bool PolymorphicAttribute::merge(void*& parent, void* children[]) const { + return false; +} diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h new file mode 100644 index 0000000000..a5f9c08f8b --- /dev/null +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -0,0 +1,275 @@ +// +// AttributeRegistry.h +// metavoxels +// +// Created by Andrzej Kapolka on 12/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__AttributeRegistry__ +#define __interface__AttributeRegistry__ + +#include +#include +#include +#include +#include +#include +#include + +#include "Bitstream.h" + +class QScriptContext; +class QScriptEngine; +class QScriptValue; + +class Attribute; + +typedef QSharedPointer AttributePointer; + +/// Maintains information about metavoxel attribute types. +class AttributeRegistry { +public: + + /// Returns a pointer to the singleton registry instance. + static AttributeRegistry* getInstance() { return &_instance; } + + AttributeRegistry(); + + /// Configures the supplied script engine with the global AttributeRegistry property. + void configureScriptEngine(QScriptEngine* engine); + + /// Registers an attribute with the system. The registry assumes ownership of the object. + /// \return either the pointer passed as an argument, if the attribute wasn't already registered, or the existing + /// attribute + AttributePointer registerAttribute(Attribute* attribute) { return registerAttribute(AttributePointer(attribute)); } + + /// Registers an attribute with the system. + /// \return either the pointer passed as an argument, if the attribute wasn't already registered, or the existing + /// attribute + AttributePointer registerAttribute(AttributePointer attribute); + + /// Retrieves an attribute by name. + AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); } + + /// Returns a reference to the standard PolymorphicDataPointer "guide" attribute. + const AttributePointer& getGuideAttribute() const { return _guideAttribute; } + + /// Returns a reference to the standard QRgb "color" attribute. + const AttributePointer& getColorAttribute() const { return _colorAttribute; } + + /// Returns a reference to the standard QRgb "normal" attribute. + const AttributePointer& getNormalAttribute() const { return _normalAttribute; } + +private: + + static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); + + static AttributeRegistry _instance; + + QHash _attributes; + AttributePointer _guideAttribute; + AttributePointer _colorAttribute; + AttributePointer _normalAttribute; +}; + +/// Converts a value to a void pointer. +template inline void* encodeInline(T value) { + return *(void**)&value; +} + +/// Extracts a value from a void pointer. +template inline T decodeInline(void* value) { + return *(T*)&value; +} + +/// Pairs an attribute value with its type. +class AttributeValue { +public: + + AttributeValue(const AttributePointer& attribute = AttributePointer()); + AttributeValue(const AttributePointer& attribute, void* value); + + AttributePointer getAttribute() const { return _attribute; } + void* getValue() const { return _value; } + + template void setInlineValue(T value) { _value = encodeInline(value); } + template T getInlineValue() const { return decodeInline(_value); } + + template T* getPointerValue() const { return static_cast(_value); } + + void* copy() const; + + bool isDefault() const; + + bool operator==(const AttributeValue& other) const; + bool operator==(void* other) const; + +protected: + + AttributePointer _attribute; + void* _value; +}; + +// Assumes ownership of an attribute value. +class OwnedAttributeValue : public AttributeValue { +public: + + OwnedAttributeValue(const AttributePointer& attribute = AttributePointer()); + OwnedAttributeValue(const AttributePointer& attribute, void* value); + OwnedAttributeValue(const AttributeValue& other); + ~OwnedAttributeValue(); + + OwnedAttributeValue& operator=(const AttributeValue& other); +}; + +/// Represents a registered attribute. +class Attribute : public QObject { + Q_OBJECT + +public: + + static const int MERGE_COUNT = 8; + + Attribute(const QString& name); + virtual ~Attribute(); + + Q_INVOKABLE QString getName() const { return objectName(); } + + void* create() const { return create(getDefaultValue()); } + virtual void* create(void* copy) const = 0; + virtual void destroy(void* value) const = 0; + + virtual bool read(Bitstream& in, void*& value) const = 0; + virtual bool write(Bitstream& out, void* value) const = 0; + + virtual bool equal(void* first, void* second) const = 0; + + /// Merges the value of a parent and its children. + /// \return whether or not the children and parent values are all equal + virtual bool merge(void*& parent, void* children[]) const = 0; + + virtual void* getDefaultValue() const = 0; + + virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); } +}; + +/// A simple attribute class that stores its values inline. +template class InlineAttribute : public Attribute { +public: + + InlineAttribute(const QString& name, const T& defaultValue = T()) : Attribute(name), _defaultValue(defaultValue) { } + + virtual void* create(void* copy) const { void* value; new (&value) T(*(T*)©); return value; } + virtual void destroy(void* value) const { ((T*)&value)->~T(); } + + virtual bool read(Bitstream& in, void*& value) const { value = getDefaultValue(); in.read(&value, bits); return false; } + virtual bool write(Bitstream& out, void* value) const { out.write(&value, bits); return false; } + + virtual bool equal(void* first, void* second) const { return decodeInline(first) == decodeInline(second); } + + virtual void* getDefaultValue() const { return encodeInline(_defaultValue); } + +private: + + T _defaultValue; +}; + +/// Provides merging using the =, ==, += and /= operators. +template class SimpleInlineAttribute : public InlineAttribute { +public: + + SimpleInlineAttribute(const QString& name, T defaultValue = T()) : InlineAttribute(name, defaultValue) { } + + virtual bool merge(void*& parent, void* children[]) const; +}; + +template inline bool SimpleInlineAttribute::merge(void*& parent, void* children[]) const { + T& merged = *(T*)&parent; + merged = decodeInline(children[0]); + bool allChildrenEqual = true; + for (int i = 1; i < Attribute::MERGE_COUNT; i++) { + merged += decodeInline(children[i]); + allChildrenEqual &= (decodeInline(children[0]) == decodeInline(children[i])); + } + merged /= Attribute::MERGE_COUNT; + return allChildrenEqual; +} + +/// Provides appropriate averaging for RGBA values. +class QRgbAttribute : public InlineAttribute { +public: + + QRgbAttribute(const QString& name, QRgb defaultValue = QRgb()); + + virtual bool merge(void*& parent, void* children[]) const; + + virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; +}; + +/// An attribute class that stores pointers to its values. +template class PointerAttribute : public Attribute { +public: + + PointerAttribute(const QString& name, T defaultValue = T()) : Attribute(name), _defaultValue(defaultValue) { } + + virtual void* create(void* copy) const { new T(*static_cast(copy)); } + virtual void destroy(void* value) const { delete static_cast(value); } + + virtual bool read(Bitstream& in, void*& value) const { in >> *static_cast(value); return true; } + virtual bool write(Bitstream& out, void* value) const { out << *static_cast(value); return true; } + + virtual bool equal(void* first, void* second) const { return *static_cast(first) == *static_cast(second); } + + virtual void* getDefaultValue() const { return const_cast((void*)&_defaultValue); } + +private: + + T _defaultValue; +}; + +/// Provides merging using the =, ==, += and /= operators. +template class SimplePointerAttribute : public PointerAttribute { +public: + + SimplePointerAttribute(const QString& name, T defaultValue = T()) : PointerAttribute(name, defaultValue) { } + + virtual bool merge(void*& parent, void* children[]) const; +}; + +template inline bool SimplePointerAttribute::merge(void*& parent, void* children[]) const { + T& merged = *static_cast(parent); + merged = *static_cast(children[0]); + bool allChildrenEqual = true; + for (int i = 1; i < Attribute::MERGE_COUNT; i++) { + merged += *static_cast(children[i]); + allChildrenEqual &= (*static_cast(children[0]) == *static_cast(children[i])); + } + merged /= Attribute::MERGE_COUNT; + return allChildrenEqual; +} + +/// Base class for polymorphic attribute data. +class PolymorphicData : public QSharedData { +public: + + virtual ~PolymorphicData(); + + /// Creates a new clone of this object. + virtual PolymorphicData* clone() const = 0; +}; + +template<> PolymorphicData* QExplicitlySharedDataPointer::clone(); + +typedef QExplicitlySharedDataPointer PolymorphicDataPointer; + +/// Provides polymorphic streaming and averaging. +class PolymorphicAttribute : public InlineAttribute { +public: + + PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue = PolymorphicDataPointer()); + + virtual bool merge(void*& parent, void* children[]) const; +}; + +#endif /* defined(__interface__AttributeRegistry__) */ diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp new file mode 100644 index 0000000000..ac91bdf767 --- /dev/null +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -0,0 +1,81 @@ +// +// Bitstream.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 12/2/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include "Bitstream.h" + +Bitstream::Bitstream(QDataStream& underlying) + : _underlying(underlying), _byte(0), _position(0) { +} + +const int BITS_IN_BYTE = 8; +const int LAST_BIT_POSITION = BITS_IN_BYTE - 1; + +Bitstream& Bitstream::write(const void* data, int bits, int offset) { + const quint8* source = (const quint8*)data; + while (bits > 0) { + int bitsToWrite = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits)); + _byte |= ((*source >> offset) & ((1 << bitsToWrite) - 1)) << _position; + if ((_position += bitsToWrite) == BITS_IN_BYTE) { + flush(); + } + if ((offset += bitsToWrite) == BITS_IN_BYTE) { + source++; + offset = 0; + } + bits -= bitsToWrite; + } + return *this; +} + +Bitstream& Bitstream::read(void* data, int bits, int offset) { + quint8* dest = (quint8*)data; + while (bits > 0) { + if (_position == 0) { + _underlying >> _byte; + } + int bitsToRead = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits)); + *dest |= ((_byte >> _position) & ((1 << bitsToRead) - 1)) << offset; + _position = (_position + bitsToRead) & LAST_BIT_POSITION; + if ((offset += bitsToRead) == BITS_IN_BYTE) { + dest++; + offset = 0; + } + bits -= bitsToRead; + } + return *this; +} + +void Bitstream::flush() { + if (_position != 0) { + _underlying << _byte; + _byte = 0; + _position = 0; + } +} + + +Bitstream& Bitstream::operator<<(bool value) { + if (value) { + _byte |= (1 << _position); + } + if (++_position == BITS_IN_BYTE) { + flush(); + } + return *this; +} + +Bitstream& Bitstream::operator>>(bool& value) { + if (_position == 0) { + _underlying >> _byte; + } + value = _byte & (1 << _position); + _position = (_position + 1) & LAST_BIT_POSITION; + return *this; +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h new file mode 100644 index 0000000000..12a1b88886 --- /dev/null +++ b/libraries/metavoxels/src/Bitstream.h @@ -0,0 +1,45 @@ +// +// Bitstream.h +// metavoxels +// +// Created by Andrzej Kapolka on 12/2/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Bitstream__ +#define __interface__Bitstream__ + +#include + +class QDataStream; + +/// A stream for bit-aligned data. +class Bitstream { +public: + + Bitstream(QDataStream& underlying); + + /// Writes a set of bits to the underlying stream. + /// \param bits the number of bits to write + /// \param offset the offset of the first bit + Bitstream& write(const void* data, int bits, int offset = 0); + + /// Reads a set of bits from the underlying stream. + /// \param bits the number of bits to read + /// \param offset the offset of the first bit + Bitstream& read(void* data, int bits, int offset = 0); + + /// Flushes any unwritten bits to the underlying stream. + void flush(); + + Bitstream& operator<<(bool value); + Bitstream& operator>>(bool& value); + +private: + + QDataStream& _underlying; + quint8 _byte; + int _position; +}; + +#endif /* defined(__interface__Bitstream__) */ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp new file mode 100644 index 0000000000..636982b63a --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -0,0 +1,281 @@ +// +// MetavoxelData.cpp +// metavoxels +// +// Created by Andrzej Kapolka on 12/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include + +#include "MetavoxelData.h" + +MetavoxelData::~MetavoxelData() { + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + it.value()->destroy(it.key()); + delete it.value(); + } +} + +void MetavoxelData::guide(MetavoxelVisitor& visitor) { + // start with the root values/defaults (plus the guide attribute) + const float TOP_LEVEL_SIZE = 1.0f; + const QVector& attributes = visitor.getAttributes(); + MetavoxelVisitation firstVisitation = { visitor, QVector(attributes.size() + 1), + { glm::vec3(), TOP_LEVEL_SIZE, QVector(attributes.size() + 1) } }; + for (int i = 0; i < attributes.size(); i++) { + MetavoxelNode* node = _roots.value(attributes[i]); + firstVisitation.nodes[i] = node; + firstVisitation.info.attributeValues[i] = node ? node->getAttributeValue(attributes[i]) : attributes[i]; + } + AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute(); + MetavoxelNode* node = _roots.value(guideAttribute); + firstVisitation.nodes.last() = node; + firstVisitation.info.attributeValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; + static_cast(firstVisitation.info.attributeValues.last().getInlineValue< + PolymorphicDataPointer>().data())->guide(firstVisitation); +} + +void MetavoxelData::setAttributeValue(const MetavoxelPath& path, const AttributeValue& attributeValue) { + MetavoxelNode*& node = _roots[attributeValue.getAttribute()]; + if (node == NULL) { + node = new MetavoxelNode(attributeValue.getAttribute()); + } + if (node->setAttributeValue(path, 0, attributeValue) && attributeValue.isDefault()) { + node->destroy(attributeValue.getAttribute()); + delete node; + _roots.remove(attributeValue.getAttribute()); + } +} + +AttributeValue MetavoxelData::getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const { + MetavoxelNode* node = _roots.value(attribute); + if (node == NULL) { + return AttributeValue(attribute); + } + for (int i = 0, n = path.getSize(); i < n; i++) { + MetavoxelNode* child = node->getChild(path[i]); + if (child == NULL) { + return node->getAttributeValue(attribute); + } + node = child; + } + return node->getAttributeValue(attribute); +} + +MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) { + _attributeValue = attributeValue.copy(); + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i] = NULL; + } +} + +bool MetavoxelNode::setAttributeValue(const MetavoxelPath& path, int index, const AttributeValue& attributeValue) { + if (index == path.getSize()) { + setAttributeValue(attributeValue); + return true; + } + int element = path[index]; + if (_children[element] == NULL) { + AttributeValue ownAttributeValue = getAttributeValue(attributeValue.getAttribute()); + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i] = new MetavoxelNode(ownAttributeValue); + } + } + _children[element]->setAttributeValue(path, index + 1, attributeValue); + + void* childValues[CHILD_COUNT]; + bool allLeaves = true; + for (int i = 0; i < CHILD_COUNT; i++) { + childValues[i] = _children[i]->_attributeValue; + allLeaves &= _children[i]->isLeaf(); + } + if (attributeValue.getAttribute()->merge(_attributeValue, childValues) && allLeaves) { + clearChildren(attributeValue.getAttribute()); + return true; + } + + return false; +} + +void MetavoxelNode::setAttributeValue(const AttributeValue& attributeValue) { + attributeValue.getAttribute()->destroy(_attributeValue); + _attributeValue = attributeValue.copy(); + clearChildren(attributeValue.getAttribute()); +} + +AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribute) const { + return AttributeValue(attribute, _attributeValue); +} + +bool MetavoxelNode::isLeaf() const { + for (int i = 0; i < CHILD_COUNT; i++) { + if (_children[i]) { + return false; + } + } + return true; +} + +void MetavoxelNode::destroy(const AttributePointer& attribute) { + attribute->destroy(_attributeValue); + for (int i = 0; i < CHILD_COUNT; i++) { + if (_children[i]) { + _children[i]->destroy(attribute); + delete _children[i]; + } + } +} + +void MetavoxelNode::clearChildren(const AttributePointer& attribute) { + for (int i = 0; i < CHILD_COUNT; i++) { + if (_children[i]) { + _children[i]->destroy(attribute); + delete _children[i]; + _children[i] = NULL; + } + } +} + +int MetavoxelPath::operator[](int index) const { + return _array.at(index * BITS_PER_ELEMENT) | (_array.at(index * BITS_PER_ELEMENT + 1) << 1) | + (_array.at(index * BITS_PER_ELEMENT + 2) << 2); +} + +MetavoxelPath& MetavoxelPath::operator+=(int element) { + int offset = _array.size(); + _array.resize(offset + BITS_PER_ELEMENT); + _array.setBit(offset, element & 0x01); + _array.setBit(offset + 1, (element >> 1) & 0x01); + _array.setBit(offset + 2, element >> 2); + return *this; +} + +PolymorphicData* DefaultMetavoxelGuide::clone() const { + return new DefaultMetavoxelGuide(); +} + +const int X_MAXIMUM_FLAG = 1; +const int Y_MAXIMUM_FLAG = 2; +const int Z_MAXIMUM_FLAG = 4; + +void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { + visitation.info.isLeaf = visitation.allNodesLeaves(); + if (!visitation.visitor.visit(visitation.info) || visitation.info.isLeaf) { + return; + } + MetavoxelVisitation nextVisitation = { visitation.visitor, QVector(visitation.nodes.size()), + { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.nodes.size()) } }; + for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + for (int j = 0; j < visitation.nodes.size(); j++) { + MetavoxelNode* node = visitation.nodes.at(j); + MetavoxelNode* child = node ? node->getChild(i) : NULL; + nextVisitation.info.attributeValues[j] = ((nextVisitation.nodes[j] = child)) ? + child->getAttributeValue(visitation.info.attributeValues[j].getAttribute()) : + visitation.info.attributeValues[j]; + } + nextVisitation.info.minimum = visitation.info.minimum + glm::vec3( + (i & X_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, + (i & Y_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, + (i & Z_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f); + static_cast(nextVisitation.info.attributeValues.last().getInlineValue< + PolymorphicDataPointer>().data())->guide(nextVisitation); + } +} + +QScriptValue ScriptedMetavoxelGuide::getAttributes(QScriptContext* context, QScriptEngine* engine) { + ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); + + const QVector& attributes = guide->_visitation->visitor.getAttributes(); + QScriptValue attributesValue = engine->newArray(attributes.size()); + for (int i = 0; i < attributes.size(); i++) { + attributesValue.setProperty(i, engine->newQObject(attributes.at(i).data(), QScriptEngine::QtOwnership, + QScriptEngine::PreferExistingWrapperObject)); + } + + return attributesValue; +} + +QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) { + ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); + + // start with the basics, including inherited attribute values + QScriptValue infoValue = context->argument(0); + QScriptValue minimum = infoValue.property(guide->_minimumHandle); + MetavoxelInfo info = { + glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()), + infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.attributeValues, + infoValue.property(guide->_isLeafHandle).toBool() }; + + // extract and convert the values provided by the script + QScriptValue attributeValues = infoValue.property(guide->_attributeValuesHandle); + const QVector& attributes = guide->_visitation->visitor.getAttributes(); + for (int i = 0; i < attributes.size(); i++) { + QScriptValue attributeValue = attributeValues.property(i); + if (attributeValue.isValid()) { + info.attributeValues[i] = AttributeValue(attributes.at(i), + attributes.at(i)->createFromScript(attributeValue, engine)); + } + } + + QScriptValue result = guide->_visitation->visitor.visit(info); + + // destroy any created values + for (int i = 0; i < attributes.size(); i++) { + if (attributeValues.property(i).isValid()) { + info.attributeValues[i].getAttribute()->destroy(info.attributeValues[i].getValue()); + } + } + + return result; +} + +ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction) : + _guideFunction(guideFunction), + _minimumHandle(guideFunction.engine()->toStringHandle("minimum")), + _sizeHandle(guideFunction.engine()->toStringHandle("size")), + _attributeValuesHandle(guideFunction.engine()->toStringHandle("attributeValues")), + _isLeafHandle(guideFunction.engine()->toStringHandle("isLeaf")), + _getAttributesFunction(guideFunction.engine()->newFunction(getAttributes, 0)), + _visitFunction(guideFunction.engine()->newFunction(visit, 1)), + _info(guideFunction.engine()->newObject()), + _minimum(guideFunction.engine()->newArray(3)) { + + _arguments.append(guideFunction.engine()->newObject()); + QScriptValue visitor = guideFunction.engine()->newObject(); + visitor.setProperty("getAttributes", _getAttributesFunction); + visitor.setProperty("visit", _visitFunction); + _arguments[0].setProperty("visitor", visitor); + _arguments[0].setProperty("info", _info); + _info.setProperty(_minimumHandle, _minimum); +} + +PolymorphicData* ScriptedMetavoxelGuide::clone() const { + return new ScriptedMetavoxelGuide(_guideFunction); +} + +void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { + QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue(this)); + _getAttributesFunction.setData(data); + _visitFunction.setData(data); + _minimum.setProperty(0, visitation.info.minimum.x); + _minimum.setProperty(1, visitation.info.minimum.y); + _minimum.setProperty(2, visitation.info.minimum.z); + _info.setProperty(_sizeHandle, visitation.info.size); + _info.setProperty(_isLeafHandle, visitation.info.isLeaf); + _visitation = &visitation; + _guideFunction.call(QScriptValue(), _arguments); + if (_guideFunction.engine()->hasUncaughtException()) { + qDebug() << "Script error: " << _guideFunction.engine()->uncaughtException().toString() << "\n"; + } +} + +bool MetavoxelVisitation::allNodesLeaves() const { + foreach (MetavoxelNode* node, nodes) { + if (node != NULL && !node->isLeaf()) { + return false; + } + } + return true; +} diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h new file mode 100644 index 0000000000..fc7045cff4 --- /dev/null +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -0,0 +1,187 @@ +// +// MetavoxelData.h +// metavoxels +// +// Created by Andrzej Kapolka on 12/6/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelData__ +#define __interface__MetavoxelData__ + +#include +#include +#include +#include +#include + +#include + +#include "AttributeRegistry.h" + +class QScriptContext; + +class MetavoxelNode; +class MetavoxelPath; +class MetavoxelVisitation; +class MetavoxelVisitor; + +/// The base metavoxel representation shared between server and client. +class MetavoxelData { +public: + + ~MetavoxelData(); + + /// Applies the specified visitor to the contained voxels. + void guide(MetavoxelVisitor& visitor); + + /// Sets the attribute value corresponding to the specified path. + void setAttributeValue(const MetavoxelPath& path, const AttributeValue& attributeValue); + + /// Retrieves the attribute value corresponding to the specified path. + AttributeValue getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const; + +private: + + QHash _roots; +}; + +/// A single node within a metavoxel layer. +class MetavoxelNode { +public: + + static const int CHILD_COUNT = 8; + + MetavoxelNode(const AttributeValue& attributeValue); + + /// Descends the voxel tree in order to set the value of a node. + /// \param path the path to follow + /// \param index the position in the path + /// \return whether or not the node is entirely equal to the value + bool setAttributeValue(const MetavoxelPath& path, int index, const AttributeValue& attributeValue); + + void setAttributeValue(const AttributeValue& attributeValue); + + AttributeValue getAttributeValue(const AttributePointer& attribute) const; + + MetavoxelNode* getChild(int index) const { return _children[index]; } + void setChild(int index, MetavoxelNode* child) { _children[index] = child; } + + bool isLeaf() const; + + void destroy(const AttributePointer& attribute); + +private: + Q_DISABLE_COPY(MetavoxelNode) + + void clearChildren(const AttributePointer& attribute); + + void* _attributeValue; + MetavoxelNode* _children[CHILD_COUNT]; +}; + +/// A path down an octree. +class MetavoxelPath { +public: + + int getSize() const { return _array.size() / BITS_PER_ELEMENT; } + bool isEmpty() const { return _array.isEmpty(); } + + int operator[](int index) const; + + MetavoxelPath& operator+=(int element); + +private: + + static const int BITS_PER_ELEMENT = 3; + + QBitArray _array; +}; + +/// Contains information about a metavoxel (explicit or procedural). +class MetavoxelInfo { +public: + + glm::vec3 minimum; ///< the minimum extent of the area covered by the voxel + float size; ///< the size of the voxel in all dimensions + QVector attributeValues; + bool isLeaf; +}; + +/// Interface for visitors to metavoxels. +class MetavoxelVisitor { +public: + + MetavoxelVisitor(const QVector& attributes) : _attributes(attributes) { } + + /// Returns a reference to the list of attributes desired. + const QVector& getAttributes() const { return _attributes; } + + /// Visits a metavoxel. + /// \param info the metavoxel ata + /// \param if true, continue descending; if false, stop + virtual bool visit(const MetavoxelInfo& info) = 0; + +protected: + + QVector _attributes; +}; + +/// Interface for objects that guide metavoxel visitors. +class MetavoxelGuide : public PolymorphicData { +public: + + /// Guides the specified visitor to the contained voxels. + virtual void guide(MetavoxelVisitation& visitation) = 0; +}; + +/// Guides visitors through the explicit content of the system. +class DefaultMetavoxelGuide : public MetavoxelGuide { +public: + + virtual PolymorphicData* clone() const; + + virtual void guide(MetavoxelVisitation& visitation); +}; + +/// Represents a guide implemented in Javascript. +class ScriptedMetavoxelGuide : public MetavoxelGuide { +public: + + ScriptedMetavoxelGuide(const QScriptValue& guideFunction); + + virtual PolymorphicData* clone() const; + + virtual void guide(MetavoxelVisitation& visitation); + +private: + + static QScriptValue getAttributes(QScriptContext* context, QScriptEngine* engine); + static QScriptValue visit(QScriptContext* context, QScriptEngine* engine); + + QScriptValue _guideFunction; + QScriptString _minimumHandle; + QScriptString _sizeHandle; + QScriptString _attributeValuesHandle; + QScriptString _isLeafHandle; + QScriptValueList _arguments; + QScriptValue _getAttributesFunction; + QScriptValue _visitFunction; + QScriptValue _info; + QScriptValue _minimum; + + MetavoxelVisitation* _visitation; +}; + +/// Contains the state associated with a visit to a metavoxel system. +class MetavoxelVisitation { +public: + + MetavoxelVisitor& visitor; + QVector nodes; + MetavoxelInfo info; + + bool allNodesLeaves() const; +}; + +#endif /* defined(__interface__MetavoxelData__) */ diff --git a/libraries/octree-server/src/OctreeServer.cpp b/libraries/octree-server/src/OctreeServer.cpp index ffb5f9c976..a8010cefaf 100644 --- a/libraries/octree-server/src/OctreeServer.cpp +++ b/libraries/octree-server/src/OctreeServer.cpp @@ -656,6 +656,17 @@ void OctreeServer::run() { _persistThread->initialize(true); } } + + // Debug option to demonstrate that the server's local time does not + // need to be in sync with any other network node. This forces clock + // skew for the individual server node + const char* CLOCK_SKEW = "--clockSkew"; + const char* clockSkewOption = getCmdOption(_argc, _argv, CLOCK_SKEW); + if (clockSkewOption) { + int clockSkew = atoi(clockSkewOption); + usecTimestampNowForceClockSkew(clockSkew); + qDebug("clockSkewOption=%s clockSkew=%d\n", clockSkewOption, clockSkew); + } // Check to see if the user passed in a command line option for setting packet send rate const char* PACKETS_PER_SECOND = "--packetsPerSecond"; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index fb5d603d7d..ec0783d0d9 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -36,15 +36,16 @@ Particle::~Particle() { void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, bool inHand, QString updateScript, uint32_t id) { if (id == NEW_PARTICLE) { - _created = usecTimestampNow(); _id = _nextID; _nextID++; } else { _id = id; } - _lastUpdated = usecTimestampNow(); - _lastEdited = _lastUpdated; - + uint64_t now = usecTimestampNow(); + _edited = now; + _lastSimulated = now; + _created = now; // will get updated as appropriate in setLifetime() + _position = position; _radius = radius; memcpy(_color, color, sizeof(_color)); @@ -63,13 +64,10 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const { //printf("Particle::appendParticleData()... getID()=%d\n", getID()); if (success) { - success = packetData->appendValue(getCreated()); + success = packetData->appendValue(getLifetime()); } if (success) { - success = packetData->appendValue(getLastUpdated()); - } - if (success) { - success = packetData->appendValue(getLastEdited()); + success = packetData->appendValue(getEditedAgo()); } if (success) { success = packetData->appendValue(getRadius()); @@ -103,9 +101,30 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const { } int Particle::expectedBytes() { - int expectedBytes = sizeof(uint32_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(float) + - sizeof(glm::vec3) + sizeof(rgbColor) + sizeof(glm::vec3) + - sizeof(glm::vec3) + sizeof(float) + sizeof(bool); + int expectedBytes = sizeof(uint32_t) // id + + sizeof(float) // lifetime + + sizeof(float) // edited ago + + sizeof(float) // radius + + sizeof(glm::vec3) // position + + sizeof(rgbColor) // color + + sizeof(glm::vec3) // velocity + + sizeof(glm::vec3) // gravity + + sizeof(float) // damping + + sizeof(bool); // inhand + // potentially more... + return expectedBytes; +} + +int Particle::expectedEditMessageBytes() { + int expectedBytes = sizeof(uint32_t) // id + + sizeof(float) // radius + + sizeof(glm::vec3) // position + + sizeof(rgbColor) // color + + sizeof(glm::vec3) // velocity + + sizeof(glm::vec3) // gravity + + sizeof(float) // damping + + sizeof(bool); // inhand + // potentially more... return expectedBytes; } @@ -119,20 +138,19 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef dataAt += sizeof(_id); bytesRead += sizeof(_id); - // created - memcpy(&_created, dataAt, sizeof(_created)); - dataAt += sizeof(_created); - bytesRead += sizeof(_created); + // lifetime + float lifetime; + memcpy(&lifetime, dataAt, sizeof(lifetime)); + dataAt += sizeof(lifetime); + bytesRead += sizeof(lifetime); + setLifetime(lifetime); - // lastupdated - memcpy(&_lastUpdated, dataAt, sizeof(_lastUpdated)); - dataAt += sizeof(_lastUpdated); - bytesRead += sizeof(_lastUpdated); - - // _lastEdited - memcpy(&_lastEdited, dataAt, sizeof(_lastEdited)); - dataAt += sizeof(_lastEdited); - bytesRead += sizeof(_lastEdited); + // edited ago + float editedAgo; + memcpy(&editedAgo, dataAt, sizeof(editedAgo)); + dataAt += sizeof(editedAgo); + bytesRead += sizeof(editedAgo); + setEditedAgo(editedAgo); // radius memcpy(&_radius, dataAt, sizeof(_radius)); @@ -186,7 +204,7 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes) { - Particle newParticle; // id and lastUpdated will get set here... + Particle newParticle; // id and _lastSimulated will get set here... unsigned char* dataAt = data; processedBytes = 0; @@ -214,26 +232,17 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe processedBytes += sizeof(creatorTokenID); newParticle.setCreatorTokenID(creatorTokenID); newParticle._newlyCreated = true; + + newParticle.setLifetime(0); // this guy is new! + } else { newParticle._id = editID; newParticle._newlyCreated = false; } - // created - memcpy(&newParticle._created, dataAt, sizeof(newParticle._created)); - dataAt += sizeof(newParticle._created); - processedBytes += sizeof(newParticle._created); - - // lastUpdated - memcpy(&newParticle._lastUpdated, dataAt, sizeof(newParticle._lastUpdated)); - dataAt += sizeof(newParticle._lastUpdated); - processedBytes += sizeof(newParticle._lastUpdated); - - // lastEdited - memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited)); - dataAt += sizeof(newParticle._lastEdited); - processedBytes += sizeof(newParticle._lastEdited); - + // clearly we just edited it + newParticle.setEditedAgo(0); + // radius memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius)); dataAt += sizeof(newParticle._radius); @@ -291,9 +300,8 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe void Particle::debugDump() const { printf("Particle id :%u\n", _id); - printf(" created:%llu\n", _created); - printf(" last updated:%llu\n", _lastUpdated); - printf(" last edited:%llu\n", _lastEdited); + printf(" lifetime:%f\n", getLifetime()); + printf(" edited ago:%f\n", getEditedAgo()); printf(" position:%f,%f,%f\n", _position.x, _position.y, _position.z); printf(" velocity:%f,%f,%f\n", _velocity.x, _velocity.y, _velocity.z); printf(" gravity:%f,%f,%f\n", _gravity.x, _gravity.y, _gravity.z); @@ -313,7 +321,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, int octets = numberOfThreeBitSectionsInCode(octcode); int lengthOfOctcode = bytesRequiredForCodeLength(octets); - int lenfthOfEditData = lengthOfOctcode + expectedBytes(); + int lenfthOfEditData = lengthOfOctcode + expectedEditMessageBytes(); // make sure we have room to copy this particle if (sizeOut + lenfthOfEditData > sizeIn) { @@ -325,7 +333,6 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, sizeOut += lengthOfOctcode; // Now add our edit content details... - uint64_t created = usecTimestampNow(); // id memcpy(copyAt, &details[i].id, sizeof(details[i].id)); @@ -338,25 +345,8 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, memcpy(copyAt, &details[i].creatorTokenID, sizeof(details[i].creatorTokenID)); copyAt += sizeof(details[i].creatorTokenID); sizeOut += sizeof(details[i].creatorTokenID); - } else { - created = 0; } - // created - memcpy(copyAt, &created, sizeof(created)); - copyAt += sizeof(created); - sizeOut += sizeof(created); - - // lastUpdated - memcpy(copyAt, &details[i].lastUpdated, sizeof(details[i].lastUpdated)); - copyAt += sizeof(details[i].lastUpdated); - sizeOut += sizeof(details[i].lastUpdated); - - // lastEdited - memcpy(copyAt, &details[i].lastEdited, sizeof(details[i].lastEdited)); - copyAt += sizeof(details[i].lastEdited); - sizeOut += sizeof(details[i].lastEdited); - // radius memcpy(copyAt, &details[i].radius, sizeof(details[i].radius)); copyAt += sizeof(details[i].radius); @@ -405,7 +395,6 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, if (wantDebugging) { printf("encodeParticleEditMessageDetails()....\n"); printf("Particle id :%u\n", details[i].id); - printf(" last updated:%llu\n", details[i].lastUpdated); printf(" nextID:%u\n", _nextID); } } @@ -418,21 +407,30 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, void Particle::update() { + uint64_t now = usecTimestampNow(); - uint64_t elapsed = now - _lastUpdated; - uint64_t USECS_PER_SECOND = 1000 * 1000; + uint64_t elapsed = now - _lastSimulated; float timeElapsed = (float)((float)elapsed/(float)USECS_PER_SECOND); + // calculate our default shouldDie state... then allow script to change it if it wants... float velocityScalar = glm::length(getVelocity()); const float STILL_MOVING = 0.05 / TREE_SCALE; bool isStillMoving = (velocityScalar > STILL_MOVING); - const uint64_t REALLY_OLD = 30 * 1000 * 1000; + const float REALLY_OLD = 30.0f; // 30 seconds bool isReallyOld = (getLifetime() > REALLY_OLD); bool isInHand = getInHand(); bool shouldDie = !isInHand && !isStillMoving && isReallyOld; setShouldDie(shouldDie); - + + bool wantDebug = false; + if (wantDebug) { + printf("Particle::update()... timeElapsed: %f lifeTime:%f editedAgo:%f " + "isInHand:%s isStillMoveing:%s isReallyOld:%s shouldDie:%s\n", + timeElapsed, getLifetime(), getEditedAgo(), debug::valueOf(isInHand), debug::valueOf(isStillMoving), + debug::valueOf(isReallyOld), debug::valueOf(shouldDie)); + } + runScript(); // allow the javascript to alter our state // If the ball is in hand, it doesn't move or have gravity effect it @@ -454,7 +452,7 @@ void Particle::update() { //printf("applying damping to Particle timeElapsed=%f\n",timeElapsed); } - _lastUpdated = now; + _lastSimulated = now; } void Particle::runScript() { @@ -484,3 +482,19 @@ void Particle::runScript() { } } } + +void Particle::setLifetime(float lifetime) { + uint64_t lifetimeInUsecs = lifetime * USECS_PER_SECOND; + _created = usecTimestampNow() - lifetimeInUsecs; +} + +void Particle::setEditedAgo(float editedAgo) { + uint64_t editedAgoInUsecs = editedAgo * USECS_PER_SECOND; + _edited = usecTimestampNow() - editedAgoInUsecs; +} + +void Particle::copyChangedProperties(const Particle& other) { + float lifetime = getLifetime(); + *this = other; + setLifetime(lifetime); +} diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index 33e8960670..5090828724 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -25,8 +25,6 @@ const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF; class ParticleDetail { public: uint32_t id; - uint64_t lastUpdated; - uint64_t lastEdited; glm::vec3 position; float radius; rgbColor color; @@ -68,10 +66,11 @@ public: const glm::vec3& getGravity() const { return _gravity; } bool getInHand() const { return _inHand; } float getDamping() const { return _damping; } - uint64_t getCreated() const { return _created; } - uint64_t getLifetime() const { return usecTimestampNow() - _created; } - uint64_t getLastUpdated() const { return _lastUpdated; } - uint64_t getLastEdited() const { return _lastEdited; } + + /// lifetime of the particle in seconds + float getLifetime() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; } + /// seconds since last edited + float getEditedAgo() const { return (float)(usecTimestampNow() - _edited) / (float)USECS_PER_SECOND; } uint32_t getID() const { return _id; } bool getShouldDie() const { return _shouldDie; } QString getUpdateScript() const { return _updateScript; } @@ -93,11 +92,11 @@ public: void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; } void setUpdateScript(QString updateScript) { _updateScript = updateScript; } void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; } - void setCreated(uint64_t created) { _created = created; } bool appendParticleData(OctreePacketData* packetData) const; int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); static int expectedBytes(); + static int expectedEditMessageBytes(); static bool encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, unsigned char* bufferOut, int sizeIn, int& sizeOut); @@ -105,20 +104,24 @@ public: void update(); void debugDump() const; + + // similar to assignment/copy, but it handles keeping lifetime accurate + void copyChangedProperties(const Particle& other); + protected: void runScript(); static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3); static void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); static QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color); static void xColorFromScriptValue(const QScriptValue &object, xColor& color); + + void setLifetime(float lifetime); + void setEditedAgo(float editedAgo); glm::vec3 _position; rgbColor _color; float _radius; glm::vec3 _velocity; - uint64_t _lastUpdated; - uint64_t _created; - uint64_t _lastEdited; uint32_t _id; static uint32_t _nextID; bool _shouldDie; @@ -129,6 +132,12 @@ protected: uint32_t _creatorTokenID; bool _newlyCreated; + + // these are never included in wire time + uint64_t _lastSimulated; + uint64_t _created; + uint64_t _edited; + }; class ParticleScriptObject : public QObject { @@ -144,9 +153,7 @@ public slots: float getDamping() const { return _particle->getDamping(); } float getRadius() const { return _particle->getRadius(); } bool getShouldDie() { return _particle->getShouldDie(); } - float getCreated() const { return ((float)_particle->getCreated() / (float)USECS_PER_SECOND); } - float getLifetime() const { return ((float)_particle->getLifetime() / (float)USECS_PER_SECOND); } - + float getLifetime() const { return _particle->getLifetime(); } void setPosition(glm::vec3 value) { _particle->setPosition(value); } void setVelocity(glm::vec3 value) { _particle->setVelocity(value); } diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 1a21d1231e..8281deb12d 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -206,7 +206,7 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm:: void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency) { // consider whether to have the collision make a sound - const float AUDIBLE_COLLISION_THRESHOLD = 0.02f; + const float AUDIBLE_COLLISION_THRESHOLD = 0.1f; const float COLLISION_LOUDNESS = 1.f; const float DURATION_SCALING = 0.004f; const float NOISE_SCALING = 0.1f; @@ -235,6 +235,6 @@ void ParticleCollisionSystem::updateCollisionSound(Particle* particle, const glm fmin(COLLISION_LOUDNESS * velocityTowardCollision, 1.f), frequency * (1.f + velocityTangentToCollision / velocityTowardCollision), fmin(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f), - 1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true); + 1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, false); } } \ No newline at end of file diff --git a/libraries/particles/src/ParticleEditHandle.cpp b/libraries/particles/src/ParticleEditHandle.cpp index 28356ce63e..d8466816c4 100644 --- a/libraries/particles/src/ParticleEditHandle.cpp +++ b/libraries/particles/src/ParticleEditHandle.cpp @@ -44,8 +44,7 @@ void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor glm::vec3 gravity, float damping, bool inHand, QString updateScript) { // setup a ParticleDetail struct with the data - uint64_t now = usecTimestampNow(); - ParticleDetail addParticleDetail = { NEW_PARTICLE, now, now, + ParticleDetail addParticleDetail = { NEW_PARTICLE, position, radius, {color.red, color.green, color.blue }, velocity, gravity, damping, inHand, updateScript, _creatorTokenID }; @@ -70,8 +69,7 @@ bool ParticleEditHandle::updateParticle(glm::vec3 position, float radius, xColor } // setup a ParticleDetail struct with the data - uint64_t now = usecTimestampNow(); - ParticleDetail newParticleDetail = { _id, now, now, + ParticleDetail newParticleDetail = { _id, position, radius, {color.red, color.green, color.blue }, velocity, gravity, damping, inHand, updateScript, _creatorTokenID }; diff --git a/libraries/particles/src/ParticleScriptingInterface.cpp b/libraries/particles/src/ParticleScriptingInterface.cpp index ec8209208c..758b50035d 100644 --- a/libraries/particles/src/ParticleScriptingInterface.cpp +++ b/libraries/particles/src/ParticleScriptingInterface.cpp @@ -22,8 +22,7 @@ unsigned int ParticleScriptingInterface::queueParticleAdd(glm::vec3 position, fl _nextCreatorTokenID++; // setup a ParticleDetail struct with the data - uint64_t now = usecTimestampNow(); - ParticleDetail addParticleDetail = { NEW_PARTICLE, now, now, + ParticleDetail addParticleDetail = { NEW_PARTICLE, position, radius, {color.red, color.green, color.blue }, velocity, gravity, damping, inHand, updateScript, creatorTokenID }; diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index a631eae926..b142c1f568 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -119,33 +119,19 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) { uint16_t numberOfParticles = _particles.size(); for (uint16_t i = 0; i < numberOfParticles; i++) { if (_particles[i].getID() == particle.getID()) { - int difference = _particles[i].getLastUpdated() - particle.getLastUpdated(); - - bool changedOnServer = _particles[i].getLastEdited() < particle.getLastEdited(); - bool localOlder = _particles[i].getLastUpdated() < particle.getLastUpdated(); - - if (changedOnServer || localOlder) { - + bool changedOnServer = _particles[i].getEditedAgo() > particle.getEditedAgo(); + if (changedOnServer) { if (wantDebug) { - printf("local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n", + printf("local particle [id:%d] %s, particle.isNewlyCreated()=%s\n", particle.getID(), (changedOnServer ? "CHANGED" : "same"), - (localOlder ? "OLDER" : "NEWER"), - difference, debug::valueOf(particle.isNewlyCreated()) ); + debug::valueOf(particle.isNewlyCreated()) ); } - - uint64_t actuallyCreated = particle.getCreated(); - if (!particle.isNewlyCreated()) { - actuallyCreated = _particles[i].getCreated(); - } - _particles[i] = particle; - _particles[i].setCreated(actuallyCreated); + _particles[i].copyChangedProperties(particle); } else { if (wantDebug) { - printf(">>> NO CHANGE <<< -- local particle [id:%d] %s and %s than server particle by %d, " - "particle.isNewlyCreated()=%s\n", + printf(">>> NO CHANGE <<< -- local particle [id:%d] %s particle.isNewlyCreated()=%s\n", particle.getID(), (changedOnServer ? "CHANGED" : "same"), - (localOlder ? "OLDER" : "NEWER"), - difference, debug::valueOf(particle.isNewlyCreated()) ); + debug::valueOf(particle.isNewlyCreated()) ); } } return true; diff --git a/libraries/scriptengine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt similarity index 96% rename from libraries/scriptengine/CMakeLists.txt rename to libraries/script-engine/CMakeLists.txt index 593feab014..59f41b8cbe 100644 --- a/libraries/scriptengine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -6,7 +6,7 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros) # setup for find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") -set(TARGET_NAME scriptengine) +set(TARGET_NAME script-engine) find_package(Qt5Widgets REQUIRED) diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h new file mode 100644 index 0000000000..5c791af0a4 --- /dev/null +++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h @@ -0,0 +1,38 @@ +// +// AbstractControllerScriptingInterface.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/17/13 +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__AbstractControllerScriptingInterface__ +#define __hifi__AbstractControllerScriptingInterface__ + +#include +#include + +/// handles scripting of input controller commands from JS +class AbstractControllerScriptingInterface : public QObject { + Q_OBJECT + +public slots: + virtual bool isPrimaryButtonPressed() const = 0; + virtual glm::vec2 getPrimaryJoystickPosition() const = 0; + + virtual int getNumberOfButtons() const = 0; + virtual bool isButtonPressed(int buttonIndex) const = 0; + + virtual int getNumberOfTriggers() const = 0; + virtual float getTriggerValue(int triggerIndex) const = 0; + + virtual int getNumberOfJoysticks() const = 0; + virtual glm::vec2 getJoystickPosition(int joystickIndex) const = 0; + + virtual int getNumberOfSpatialControls() const = 0; + virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0; + virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0; + virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0; +}; + +#endif /* defined(__hifi__AbstractControllerScriptingInterface__) */ diff --git a/libraries/scriptengine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp similarity index 81% rename from libraries/scriptengine/src/ScriptEngine.cpp rename to libraries/script-engine/src/ScriptEngine.cpp index 7e5f414dee..24c4fcf1d9 100644 --- a/libraries/scriptengine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -24,10 +24,14 @@ int ScriptEngine::_scriptNumber = 1; -ScriptEngine::ScriptEngine(QString scriptContents, bool wantMenuItems, - const char* scriptMenuName, AbstractMenuInterface* menu) { +ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, + const char* scriptMenuName, AbstractMenuInterface* menu, + AbstractControllerScriptingInterface* controllerScriptingInterface) { _scriptContents = scriptContents; _isFinished = false; + _isRunning = false; + + // some clients will use these menu features _wantMenuItems = wantMenuItems; if (scriptMenuName) { _scriptMenuName = "Stop "; @@ -38,6 +42,7 @@ ScriptEngine::ScriptEngine(QString scriptContents, bool wantMenuItems, _scriptMenuName.append(_scriptNumber); } _menu = menu; + _controllerScriptingInterface = controllerScriptingInterface; } ScriptEngine::~ScriptEngine() { @@ -57,10 +62,16 @@ void ScriptEngine::cleanMenuItems() { } } -void ScriptEngine::run() { +bool ScriptEngine::setScriptContents(const QString& scriptContents) { + if (_isRunning) { + return false; + } + _scriptContents = scriptContents; + return true; +} - //setupMenuItems(); - +void ScriptEngine::run() { + _isRunning = true; QScriptEngine engine; _voxelScriptingInterface.init(); @@ -78,6 +89,11 @@ void ScriptEngine::run() { QScriptValue particleScripterValue = engine.newQObject(&_particleScriptingInterface); engine.globalObject().setProperty("Particles", particleScripterValue); + if (_controllerScriptingInterface) { + QScriptValue controllerScripterValue = engine.newQObject(_controllerScriptingInterface); + engine.globalObject().setProperty("Controller", controllerScripterValue); + } + QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); @@ -107,16 +123,14 @@ void ScriptEngine::run() { if (usecToSleep > 0) { usleep(usecToSleep); } - + if (_isFinished) { - //qDebug() << "line: " << __LINE__ << " _isFinished... breaking loop\n"; break; } QCoreApplication::processEvents(); if (_isFinished) { - //qDebug() << "line: " << __LINE__ << " _isFinished... breaking loop\n"; break; } @@ -129,7 +143,9 @@ void ScriptEngine::run() { _voxelScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent - //_voxelScriptingInterface.getVoxelPacketSender()->process(); + if (!_voxelScriptingInterface.getVoxelPacketSender()->isThreaded()) { + _voxelScriptingInterface.getVoxelPacketSender()->process(); + } } if (_particleScriptingInterface.getParticlePacketSender()->serversExist()) { @@ -140,14 +156,15 @@ void ScriptEngine::run() { _particleScriptingInterface.getParticlePacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent - //_particleScriptingInterface.getParticlePacketSender()->process(); + if (!_particleScriptingInterface.getParticlePacketSender()->isThreaded()) { + _particleScriptingInterface.getParticlePacketSender()->process(); + } } if (willSendVisualDataCallBack) { emit willSendVisualDataCallback(); } - if (engine.hasUncaughtException()) { int line = engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; @@ -161,9 +178,11 @@ void ScriptEngine::run() { } emit finished(); + _isRunning = false; } void ScriptEngine::stop() { _isFinished = true; } + diff --git a/libraries/scriptengine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h similarity index 74% rename from libraries/scriptengine/src/ScriptEngine.h rename to libraries/script-engine/src/ScriptEngine.h index 772b0a146f..c5172a5772 100644 --- a/libraries/scriptengine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -18,12 +18,16 @@ #include #include #include +#include "AbstractControllerScriptingInterface.h" + +const QString NO_SCRIPT(""); class ScriptEngine : public QObject { Q_OBJECT public: - ScriptEngine(QString scriptContents, bool wantMenuItems = false, - const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL); + ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false, + const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL, + AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); ~ScriptEngine(); @@ -33,6 +37,9 @@ public: /// Access the ParticleScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener ParticleScriptingInterface* getParticleScriptingInterface() { return &_particleScriptingInterface; } + /// sets the script contents, will return false if failed, will fail if script is already running + bool setScriptContents(const QString& scriptContents); + void setupMenuItems(); void cleanMenuItems(); @@ -47,11 +54,13 @@ signals: protected: QString _scriptContents; bool _isFinished; + bool _isRunning; private: VoxelScriptingInterface _voxelScriptingInterface; ParticleScriptingInterface _particleScriptingInterface; + AbstractControllerScriptingInterface* _controllerScriptingInterface; bool _wantMenuItems; QString _scriptMenuName; AbstractMenuInterface* _menu; diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index 8c05b1ff8f..1923d906bb 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -7,11 +7,12 @@ set(TARGET_NAME shared) project(${TARGET_NAME}) find_package(Qt5Network REQUIRED) +find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiLibrary.cmake) setup_hifi_library(${TARGET_NAME}) -qt5_use_modules(${TARGET_NAME} Network) +qt5_use_modules(${TARGET_NAME} Network Widgets) # include GLM include(${MACRO_DIR}/IncludeGLM.cmake) diff --git a/libraries/shared/src/AbstractMenuInterface.h b/libraries/shared/src/AbstractMenuInterface.h index 6af0ea2d00..66083e4ed4 100644 --- a/libraries/shared/src/AbstractMenuInterface.h +++ b/libraries/shared/src/AbstractMenuInterface.h @@ -10,9 +10,12 @@ #ifndef __hifi__AbstractMenuInterface__ #define __hifi__AbstractMenuInterface__ -#include -//#include -//#include +#include + +class QMenu; +class QString; +class QObject; +class QKeySequence; class AbstractMenuInterface { public: @@ -26,4 +29,4 @@ public: virtual void removeAction(QMenu* menu, const QString& actionName) = 0; }; -#endif /* defined(__hifi__AbstractMenuInterface__) */ \ No newline at end of file +#endif /* defined(__hifi__AbstractMenuInterface__) */ diff --git a/libraries/shared/src/GenericThread.h b/libraries/shared/src/GenericThread.h index 013b7a7936..2de9112204 100644 --- a/libraries/shared/src/GenericThread.h +++ b/libraries/shared/src/GenericThread.h @@ -33,6 +33,8 @@ public: /// Override this function to do whatever your class actually does, return false to exit thread early. virtual bool process() = 0; + bool isThreaded() const { return _isThreaded; } + protected: /// Locks all the resources of the thread. @@ -43,8 +45,6 @@ protected: bool isStillRunning() const { return !_stopThread; } - bool isThreaded() const { return _isThreaded; } - private: pthread_mutex_t _mutex; diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index e7c16367aa..24865fff5c 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -54,7 +54,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { return 2; case PACKET_TYPE_PARTICLE_DATA: - return 3; + return 4; default: return 0; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index de9fcae781..9a099b4171 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -12,6 +12,7 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); + qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue); } @@ -29,6 +30,19 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) { vec3.z = object.property("z").toVariant().toFloat(); } +QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", vec2.x); + obj.setProperty("y", vec2.y); + return obj; +} + +void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2) { + vec2.x = object.property("x").toVariant().toFloat(); + vec2.y = object.property("y").toVariant().toFloat(); +} + + QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) { QScriptValue obj = engine->newObject(); obj.setProperty("red", color.red); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 51a4562c87..b5d3d80e2f 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -17,11 +17,17 @@ #include "SharedUtil.h" Q_DECLARE_METATYPE(glm::vec3) +Q_DECLARE_METATYPE(glm::vec2) Q_DECLARE_METATYPE(xColor) void registerMetaTypes(QScriptEngine* engine); + QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3); void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); + +QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2); +void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2); + QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color); void xColorFromScriptValue(const QScriptValue &object, xColor& color); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 9a6bb8b4c3..4d178a6ee9 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -30,10 +30,15 @@ uint64_t usecTimestamp(const timeval *time) { return (time->tv_sec * 1000000 + time->tv_usec); } +int usecTimestampNowAdjust = 0; +void usecTimestampNowForceClockSkew(int clockSkew) { + ::usecTimestampNowAdjust = clockSkew; +} + uint64_t usecTimestampNow() { timeval now; gettimeofday(&now, NULL); - return (now.tv_sec * 1000000 + now.tv_usec); + return (now.tv_sec * 1000000 + now.tv_usec) + ::usecTimestampNowAdjust; } float randFloat () { diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index cd6444624f..893922ec28 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -56,6 +56,7 @@ static const uint64_t USECS_PER_SECOND = 1000 * 1000; uint64_t usecTimestamp(const timeval *time); uint64_t usecTimestampNow(); +void usecTimestampNowForceClockSkew(int clockSkew); float randFloat(); int randIntInRange (int min, int max);