diff --git a/BUILD.md b/BUILD.md index c86e34823c..9bd5da7c14 100644 --- a/BUILD.md +++ b/BUILD.md @@ -6,12 +6,12 @@ * [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1g * IMPORTANT: OpenSSL 1.0.1g is critical to avoid a security vulnerability. * [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3 -* [Bullet Physics Engine](http://bulletphysics.org) ~> 2.82 +* [Bullet Physics Engine](https://code.google.com/p/bullet/downloads/list) ~> 2.82 ### OS Specific Build Guides +* [BUILD_WIN.md](BUILD_WIN.md) - additional instructions for Windows. * [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X. * [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux. -* [BUILD_WIN.md](BUILD_WIN.md) - additional instructions for Windows. ###CMake Hifi uses CMake to generate build files and project files for your platform. diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 3ccc4881c1..5045201f62 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -1,15 +1,10 @@ Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Windows specific instructions are found in this file. -###Windows Dependencies +###Windows Specific Dependencies * [GLEW](http://glew.sourceforge.net/) ~> 1.10.0 * [freeglut MSVC](http://www.transmissionzero.co.uk/software/freeglut-devel/) ~> 2.8.1 * [zLib](http://www.zlib.net/) ~> 1.2.8 - -###Visual Studio - -Currently building on Windows has been tested using the following compilers: -* Visual Studio 2013 -* Visual Studio 2013 Express +* (remember that you need all other dependencies listed in [BUILD.md](BUILD.md)) ####Visual Studio 2013 @@ -30,14 +25,15 @@ You can use the online installer or the offline installer. If you use the offlin NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit version of libraries for interface.exe to run. The 32-bit version of the static library is the one linked by our CMake find modules. -* Download the online installer [here](http://qt-project.org/downloads) +* [Download the online installer](http://qt-project.org/downloads) * When it asks you to select components, ONLY select the following: * Qt > Qt 5.3.2 > **msvc2013 32-bit OpenGL** -* Download the offline installer [here](http://download.qt-project.org/official_releases/qt/5.3/5.3.2/qt-opensource-windows-x86-msvc2013_opengl-5.3.2.exe) +* [Download the offline installer](http://download.qt-project.org/official_releases/qt/5.3/5.3.2/qt-opensource-windows-x86-msvc2013_opengl-5.3.2.exe) Once Qt is installed, you need to manually configure the following: * Make sure the Qt runtime DLLs are loadable. You must do this before you attempt to build because some tools for the build depend on Qt. E.g., add to the PATH: `Qt\5.3.2\msvc2013_opengl\bin\`. +* Go to Control Panel > System > Advanced System Settings > Environment Variables > New ... * Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.3.2\msvc2013_opengl` directory. ###External Libraries @@ -47,6 +43,9 @@ CMake will need to know where the headers and libraries for required external de The recommended route for CMake to find the external dependencies is to place all of the dependencies in one folder and set one ENV variable - HIFI_LIB_DIR. That ENV variable should point to a directory with the following structure: root_lib_dir + -> bullet + -> include + -> lib -> freeglut -> bin -> include @@ -92,7 +91,7 @@ To prevent these problems, install OpenSSL yourself. Download the following bina * Visual C++ 2008 Redistributables * Win32 OpenSSL v1.0.1h -Install OpenSSL into the Windows system directory, to make sure that QT uses the version that you've just installed, and not some other version. +Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version. ###Intel Threading Building Blocks (TBB) @@ -111,8 +110,10 @@ Add the following environment variables (remember to substitute your own directo Add to the PATH: `%HIFI_LIB_DIR%\zlib` -Important! This should be added at the beginning of the path, not the end. That's because your -system likely has many copies of zlib1.dll, and you want High Fidelity to use the correct version. If High Fidelity picks up the wrong zlib1.dll then it might be unable to use it, and that would cause it to fail to start, showing only the cryptic error "The application was unable to start correctly: 0xc0000022". +(The PATH environment variable is where Windows looks for its DLL's and executables. There's a great tool for editing these variables with ease, [Rapid Environment Editor](http://www.rapidee.com/en/download)) + +Important! This should be added at the beginning of the path, not the end (your +system likely has many copies of zlib1.dll, and you want High Fidelity to use the correct version). If High Fidelity picks up the wrong zlib1.dll then it might be unable to use it, and that would cause it to fail to start, showing only the cryptic error "The application was unable to start correctly: 0xc0000022". ###freeglut @@ -134,10 +135,10 @@ Be careful with glm. For the folder other libraries would normally call 'include ###Bullet -Bullet 2.82 source can be downloaded [here](https://code.google.com/p/bullet/downloads/detail?name=bullet-2.82-r2704.zip). Bullet does not come with prebuilt libraries, you need to make those yourself. +Bullet 2.82 source can be [downloaded here](https://code.google.com/p/bullet/downloads/detail?name=bullet-2.82-r2704.zip). Bullet does not come with prebuilt libraries, you need to make those yourself. * Download the zip file and extract into a temporary folder -* Create a directory named cmakebuild. Bullet comes with a build\ directory by default, however, that directory is intended for use with premake, and considering premake doesn't support VS2013, I prefer to run the cmake build on its own directory. +* Create a directory named cmakebuild. Bullet comes with a build\ directory by default, however, that directory is intended for use with premake, and considering premake doesn't support VS2013, we prefer to run the cmake build on its own directory. * Make the following modifications to Bullet's source: 1. In file: Extras\HACD\hacdICHull.cpp --- in line: 17 --- insert: #include <algorithm> 2. In file: src\MiniCL\cl_MiniCL_Defs.h --- comment lines 364 to 372 @@ -162,7 +163,7 @@ You now have Bullet libraries compiled, now you need to put them in the right pl _Note that the INSTALL target should handle the copying of files into an install directory automatically, however, without modifications to Cmake, the install target didn't work right for me, please update this instructions if you get that working right - Leo <leo@highfidelity.io>_ ###Build High Fidelity using Visual Studio -Follow the same build steps from the CMake section, but pass a different generator to CMake. +Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake. cmake .. -DZLIB_LIBRARY=%ZLIB_LIBRARY% -DZLIB_INCLUDE_DIR=%ZLIB_INCLUDE_DIR% -G "Visual Studio 12" diff --git a/README.md b/README.md index 90f1ca0dc7..a4d193324d 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Any target can be terminated with Ctrl-C (SIGINT) in the associated Terminal win This assignment-client will grab one assignment from the domain-server. You can tell the assignment-client what type you want it to be with the `-t` option. You can also run an assignment-client that forks off *n* assignment-clients with the `-n` option. - ./assignment-client -n 6 + ./assignment-client -n 4 To test things out you'll want to run the Interface client. diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index eab67818c4..8e700839c1 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -171,11 +170,6 @@ void Agent::run() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkReply *reply = networkAccessManager.get(QNetworkRequest(scriptURL)); - QNetworkDiskCache* cache = new QNetworkDiskCache(); - QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache"); - networkAccessManager.setCache(cache); - qDebug() << "Downloading script at" << scriptURL.toString(); QEventLoop loop; diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 7a5ad26726..709f318d2c 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -9,10 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 791c7e37b6..3686be0f00 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -13,21 +13,22 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -1920,10 +1921,8 @@ Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileR _cookieSessionHash.insert(cookieUUID, sessionData); // persist the cookie to settings file so we can get it back on DS relaunch - QSettings localSettings; - localSettings.beginGroup(DS_SETTINGS_SESSIONS_GROUP); - QVariant sessionVariant = QVariant::fromValue(sessionData); - localSettings.setValue(cookieUUID.toString(), QVariant::fromValue(sessionData)); + QStringList path = QStringList() << DS_SETTINGS_SESSIONS_GROUP << cookieUUID.toString(); + SettingHandles::SettingHandle(path).set(QVariant::fromValue(sessionData)); // setup expiry for cookie to 1 month from today QDateTime cookieExpiry = QDateTime::currentDateTimeUtc().addMonths(1); @@ -1943,11 +1942,12 @@ Headers DomainServer::setupCookieHeadersFromProfileReply(QNetworkReply* profileR void DomainServer::loadExistingSessionsFromSettings() { // read data for existing web sessions into memory so existing sessions can be leveraged - QSettings domainServerSettings; + Settings domainServerSettings; domainServerSettings.beginGroup(DS_SETTINGS_SESSIONS_GROUP); foreach(const QString& uuidKey, domainServerSettings.childKeys()) { - _cookieSessionHash.insert(QUuid(uuidKey), domainServerSettings.value(uuidKey).value()); + _cookieSessionHash.insert(QUuid(uuidKey), + domainServerSettings.value(uuidKey).value()); qDebug() << "Pulled web session from settings - cookie UUID is" << uuidKey; } } diff --git a/examples/realsenseHands.js b/examples/controllers/RealSense/realsenseHands.js similarity index 100% rename from examples/realsenseHands.js rename to examples/controllers/RealSense/realsenseHands.js diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index e3450b708e..f3c1e14001 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -47,7 +47,6 @@ var yawFromMouse = 0; var pitchFromMouse = 0; var isMouseDown = false; -var BULLET_VELOCITY = 5.0; var MIN_THROWER_DELAY = 1000; var MAX_THROWER_DELAY = 1000; var LEFT_BUTTON_3 = 3; @@ -98,7 +97,7 @@ var reticle = Overlays.addOverlay("image", { y: screenSize.y / 2 - 16, width: 32, height: 32, - imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png", + imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png", color: { red: 255, green: 255, blue: 255}, alpha: 1 }); @@ -113,6 +112,25 @@ var offButton = Overlays.addOverlay("image", { alpha: 1 }); +var platformButton = Overlays.addOverlay("image", { + x: screenSize.x - 48, + y: 130, + width: 32, + height: 32, + imageURL: HIFI_PUBLIC_BUCKET + "images/city.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); +var gridButton = Overlays.addOverlay("image", { + x: screenSize.x - 48, + y: 164, + width: 32, + height: 32, + imageURL: HIFI_PUBLIC_BUCKET + "images/blocks.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + if (showScore) { var text = Overlays.addOverlay("text", { x: screenSize.x / 2 - 100, @@ -127,24 +145,30 @@ if (showScore) { }); } -function printVector(string, vector) { - print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); -} +var BULLET_VELOCITY = 10.0; -function shootBullet(position, velocity) { - var BULLET_SIZE = 0.07; +function shootBullet(position, velocity, grenade) { + var BULLET_SIZE = 0.10; var BULLET_LIFETIME = 10.0; - var BULLET_GRAVITY = 0.0; + var BULLET_GRAVITY = -0.25; + var GRENADE_VELOCITY = 15.0; + var GRENADE_SIZE = 0.35; + var GRENADE_GRAVITY = -9.8; + + var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity; + var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE; + var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; + bulletID = Entities.addEntity( { type: "Sphere", position: position, - dimensions: { x: BULLET_SIZE, y: BULLET_SIZE, z: BULLET_SIZE }, + dimensions: { x: bSize, y: bSize, z: bSize }, color: { red: 255, green: 0, blue: 0 }, - velocity: velocity, + velocity: bVelocity, lifetime: BULLET_LIFETIME, - gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, + gravity: { x: 0, y: bGravity, z: 0 }, damping: 0.01, - density: 5000, + density: 8000, ignoreCollisions: false, collisionsWillMove: true }); @@ -158,13 +182,15 @@ function shootBullet(position, velocity) { } // Kickback the arm + if (elbowKickAngle > 0.0) { + MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback); + } rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm"); var armRotation = MyAvatar.getJointRotation("LeftForeArm"); armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE)); MyAvatar.setJointData("LeftForeArm", armRotation); elbowKickAngle = KICKBACK_ANGLE; } - function shootTarget() { var TARGET_SIZE = 0.50; var TARGET_GRAVITY = 0.0; @@ -174,7 +200,7 @@ function shootTarget() { var DISTANCE_TO_LAUNCH_FROM = 5.0; var ANGLE_RANGE_FOR_LAUNCH = 20.0; var camera = Camera.getPosition(); - //printVector("camera", camera); + var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 }); targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); var forwardVector = Quat.getFront(targetDirection); @@ -205,6 +231,78 @@ function shootTarget() { Audio.playSound(targetLaunchSound, audioOptions); } +function makeGrid(type, scale, size) { + var separation = scale * 2; + var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); + var x, y, z; + var GRID_LIFE = 60.0; + var dimensions; + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + for (z = 0; z < size; z++) { + + dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 }; + + Entities.addEntity( + { type: type, + position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation }, + dimensions: dimensions, + color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: GRID_LIFE, + rotation: Camera.getOrientation(), + damping: 0.1, + density: 100.0, + collisionsWillMove: true }); + } + } + } +} +function makePlatform(gravity, scale, size) { + var separation = scale * 2; + var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); + pos.y -= separation * size; + var x, y, z; + var TARGET_LIFE = 60.0; + var INITIAL_GAP = 0.5; + var dimensions; + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + for (z = 0; z < size; z++) { + + dimensions = { x: separation/2.0, y: separation, z: separation/2.0 }; + + Entities.addEntity( + { type: "Box", + position: { x: pos.x - (separation * size / 2.0) + x * separation, + y: pos.y + y * (separation + INITIAL_GAP), + z: pos.z - (separation * size / 2.0) + z * separation }, + dimensions: dimensions, + color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: gravity, z: 0 }, + lifetime: TARGET_LIFE, + damping: 0.1, + density: 100.0, + collisionsWillMove: true }); + } + } + } + + // Make a floor for this stuff to fall onto + Entities.addEntity({ + type: "Box", + position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, + dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size }, + color: { red: 128, green: 128, blue: 128 }, + lifetime: TARGET_LIFE + }); + +} + function entityCollisionWithEntity(entity1, entity2, collision) { if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) && @@ -233,7 +331,9 @@ function keyPressEvent(event) { var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; Script.setTimeout(shootTarget, time); } else if ((event.text == ".") || (event.text == "SPACE")) { - shootFromMouse(); + shootFromMouse(false); + } else if (event.text == ",") { + shootFromMouse(true); } else if (event.text == "r") { playLoadSound(); } else if (event.text == "s") { @@ -241,7 +341,6 @@ function keyPressEvent(event) { Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm")); Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm")); Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand")); - } } @@ -287,18 +386,7 @@ function update(deltaTime) { if (targetID && !targetID.isKnownID) { targetID = Entities.identifyEntity(targetID); } - // Check for mouseLook movement, update rotation - // rotate body yaw for yaw received from mouse - var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } )); - //MyAvatar.orientation = newOrientation; - yawFromMouse = 0; - // apply pitch from mouse - var newPitch = MyAvatar.headPitch + pitchFromMouse; - //MyAvatar.headPitch = newPitch; - pitchFromMouse = 0; - - if (activeControllers == 0) { if (Controller.getNumberOfSpatialControls() > 0) { activeControllers = Controller.getNumberOfSpatialControls(); @@ -372,19 +460,19 @@ function update(deltaTime) { var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector)); - shootBullet(position, velocity); + shootBullet(position, velocity, false); } } } } -function shootFromMouse() { +function shootFromMouse(grenade) { var DISTANCE_FROM_CAMERA = 1.0; var camera = Camera.getPosition(); var forwardVector = Quat.getFront(Camera.getOrientation()); var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); - shootBullet(newPosition, velocity); + shootBullet(newPosition, velocity, grenade); } function mouseReleaseEvent(event) { @@ -392,21 +480,24 @@ function mouseReleaseEvent(event) { isMouseDown = false; } -function mouseMoveEvent(event) { - if (isMouseDown) { - var MOUSE_YAW_SCALE = -0.25; - var MOUSE_PITCH_SCALE = -12.5; - var FIXED_MOUSE_TIMESTEP = 0.016; - yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP); - pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); - lastX = event.x; - lastY = event.y; - } +function mousePressEvent(event) { + var clickedText = false; + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + if (clickedOverlay == offButton) { + Script.stop(); + } else if (clickedOverlay == platformButton) { + var platformSize = 3; + makePlatform(-9.8, 1.0, platformSize); + } else if (clickedOverlay == gridButton) { + makeGrid("Box", 1.0, 3); + } } function scriptEnding() { Overlays.deleteOverlay(reticle); Overlays.deleteOverlay(offButton); + Overlays.deleteOverlay(platformButton); + Overlays.deleteOverlay(gridButton); Overlays.deleteOverlay(pointer[0]); Overlays.deleteOverlay(pointer[1]); Overlays.deleteOverlay(text); @@ -418,7 +509,7 @@ Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity); Script.scriptEnding.connect(scriptEnding); Script.update.connect(update); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); -Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mousePressEvent.connect(mousePressEvent); Controller.keyPressEvent.connect(keyPressEvent); diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 4d0b873fd2..202a226f1e 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -14,13 +14,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("libraries/entityPropertyDialogBox.js"); +Script.include("../../libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; -var LASER_WIDTH = 4; -var LASER_COLOR = { red: 255, green: 0, blue: 0 }; -var LASER_LENGTH_FACTOR = 500; - var MIN_ANGULAR_SIZE = 2; var MAX_ANGULAR_SIZE = 45; var allowLargeModels = false; @@ -32,7 +28,44 @@ var RIGHT = 1; var jointList = MyAvatar.getJointNames(); -var mode = 0; +var STICKS = 0; +var MAPPED = 1; +var mode = STICKS; + +var LASER_WIDTH = 4; +var LASER_COLOR = [{ red: 200, green: 150, blue: 50 }, // STICKS + { red: 50, green: 150, blue: 200 }]; // MAPPED +var LASER_LENGTH_FACTOR = 500; + +var lastAccurateIntersection = null; +var accurateIntersections = 0; +var totalIntersections = 0; +var inaccurateInARow = 0; +var maxInaccurateInARow = 0; +function getRayIntersection(pickRay) { // pickRay : { origin : {xyz}, direction : {xyz} } + if (lastAccurateIntersection === null) { + lastAccurateIntersection = Entities.findRayIntersectionBlocking(pickRay); + } else { + var intersection = Entities.findRayIntersection(pickRay); + if (intersection.accurate) { + lastAccurateIntersection = intersection; + accurateIntersections++; + maxInaccurateInARow = (maxInaccurateInARow > inaccurateInARow) ? maxInaccurateInARow : inaccurateInARow; + inaccurateInARow = 0; + } else { + inaccurateInARow++; + } + totalIntersections++; + } + return lastAccurateIntersection; +} + +function printIntersectionsStats() { + var ratio = accurateIntersections / totalIntersections; + print("Out of " + totalIntersections + " intersections, " + accurateIntersections + " where accurate. (" + ratio * 100 +"%)"); + print("Worst case was " + maxInaccurateInARow + " inaccurate intersections in a row."); +} + function controller(wichSide) { this.side = wichSide; @@ -42,10 +75,10 @@ function controller(wichSide) { this.bumper = 6 * wichSide + 5; this.oldPalmPosition = Controller.getSpatialControlPosition(this.palm); - this.palmPosition = Controller.getSpatialControlPosition(this.palm); + this.palmPosition = this.oldPalmPosition; this.oldTipPosition = Controller.getSpatialControlPosition(this.tip); - this.tipPosition = Controller.getSpatialControlPosition(this.tip); + this.tipPosition = this.oldTipPosition; this.oldUp = Controller.getSpatialControlNormal(this.palm); this.up = this.oldUp; @@ -75,13 +108,13 @@ function controller(wichSide) { this.positionAtGrab; this.rotationAtGrab; this.modelPositionAtGrab; - this.rotationAtGrab; + this.modelRotationAtGrab; this.jointsIntersectingFromStart = []; this.laser = Overlays.addOverlay("line3d", { start: { x: 0, y: 0, z: 0 }, end: { x: 0, y: 0, z: 0 }, - color: LASER_COLOR, + color: LASER_COLOR[mode], alpha: 1, visible: false, lineWidth: LASER_WIDTH, @@ -132,7 +165,7 @@ function controller(wichSide) { this.positionAtGrab = this.palmPosition; this.rotationAtGrab = this.rotation; this.modelPositionAtGrab = properties.position; - this.rotationAtGrab = properties.rotation; + this.modelRotationAtGrab = properties.rotation; this.jointsIntersectingFromStart = []; for (var i = 0; i < jointList.length; i++) { var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition); @@ -245,9 +278,9 @@ function controller(wichSide) { var inverseRotation = Quat.inverse(MyAvatar.orientation); var startPosition = Vec3.multiplyQbyV(inverseRotation, Vec3.subtract(this.palmPosition, MyAvatar.position)); + startPosition = Vec3.multiply(startPosition, 1 / MyAvatar.scale); var direction = Vec3.multiplyQbyV(inverseRotation, Vec3.subtract(this.tipPosition, this.palmPosition)); - var distance = Vec3.length(direction); - direction = Vec3.multiply(direction, LASER_LENGTH_FACTOR / distance); + direction = Vec3.multiply(direction, LASER_LENGTH_FACTOR / (Vec3.length(direction) * MyAvatar.scale)); var endPosition = Vec3.sum(startPosition, direction); Overlays.editOverlay(this.laser, { @@ -267,17 +300,16 @@ function controller(wichSide) { start: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)), end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)) }); - this.showLaser(!this.grabbing || mode == 0); + this.showLaser(!this.grabbing || mode == STICKS); if (this.glowedIntersectingModel.isKnownID) { Entities.editEntity(this.glowedIntersectingModel, { glowLevel: 0.0 }); this.glowedIntersectingModel.isKnownID = false; } if (!this.grabbing) { - var intersection = Entities.findRayIntersection({ - origin: this.palmPosition, - direction: this.front - }); + var intersection = getRayIntersection({ origin: this.palmPosition, + direction: this.front + }); var halfDiagonal = Vec3.length(intersection.properties.dimensions) / 2.0; @@ -304,17 +336,16 @@ function controller(wichSide) { if (this.grabbing) { if (!this.entityID.isKnownID) { print("Unknown grabbed ID " + this.entityID.id + ", isKnown: " + this.entityID.isKnownID); - this.entityID = Entities.findRayIntersection({ - origin: this.palmPosition, - direction: this.front - }).entityID; + this.entityID = getRayIntersection({ origin: this.palmPosition, + direction: this.front + }).entityID; print("Identified ID " + this.entityID.id + ", isKnown: " + this.entityID.isKnownID); } var newPosition; var newRotation; switch (mode) { - case 0: + case STICKS: newPosition = Vec3.sum(this.palmPosition, Vec3.multiply(this.front, this.x)); newPosition = Vec3.sum(newPosition, @@ -328,7 +359,7 @@ function controller(wichSide) { newRotation = Quat.multiply(newRotation, this.oldModelRotation); break; - case 1: + case MAPPED: var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); var d = Vec3.dot(forward, MyAvatar.position); @@ -350,8 +381,9 @@ function controller(wichSide) { newRotation = Quat.multiply(this.rotation, Quat.inverse(this.rotationAtGrab)); + newRotation = Quat.multiply(newRotation, newRotation); newRotation = Quat.multiply(newRotation, - this.rotationAtGrab); + this.modelRotationAtGrab); break; } Entities.editEntity(this.entityID, { @@ -397,15 +429,13 @@ function controller(wichSide) { var bumperValue = Controller.isButtonPressed(this.bumper); if (bumperValue && !this.bumperValue) { - if (mode == 0) { - mode = 1; - Overlays.editOverlay(leftController.laser, { color: { red: 0, green: 0, blue: 255 } }); - Overlays.editOverlay(rightController.laser, { color: { red: 0, green: 0, blue: 255 } }); - } else { - mode = 0; - Overlays.editOverlay(leftController.laser, { color: { red: 255, green: 0, blue: 0 } }); - Overlays.editOverlay(rightController.laser, { color: { red: 255, green: 0, blue: 0 } }); + if (mode === STICKS) { + mode = MAPPED; + } else if (mode === MAPPED) { + mode = STICKS; } + Overlays.editOverlay(leftController.laser, { color: LASER_COLOR[mode] }); + Overlays.editOverlay(rightController.laser, { color: LASER_COLOR[mode] }); } this.bumperValue = bumperValue; @@ -475,10 +505,10 @@ function controller(wichSide) { Vec3.print("Looking at: ", this.palmPosition); var pickRay = { origin: this.palmPosition, direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) }; - var foundIntersection = Entities.findRayIntersection(pickRay); + var foundIntersection = getRayIntersection(pickRay); - if(!foundIntersection.accurate) { - print("No accurate intersection"); + if(!foundIntersection.intersects) { + print("No intersection"); return; } newModel = foundIntersection.entityID; @@ -526,7 +556,7 @@ function moveEntities() { switch (mode) { - case 0: + case STICKS: var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x)); var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x)); @@ -545,7 +575,7 @@ function moveEntities() { newPosition = Vec3.sum(middle, Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio)); break; - case 1: + case MAPPED: var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition)); var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition)); @@ -566,11 +596,11 @@ function moveEntities() { leftController.positionAtGrab = leftController.palmPosition; leftController.rotationAtGrab = leftController.rotation; leftController.modelPositionAtGrab = leftController.oldModelPosition; - leftController.rotationAtGrab = rotation; + leftController.modelRotationAtGrab = rotation; rightController.positionAtGrab = rightController.palmPosition; rightController.rotationAtGrab = rightController.rotation; rightController.modelPositionAtGrab = rightController.oldModelPosition; - rightController.rotationAtGrab = rotation; + rightController.modelRotationAtGrab = rotation; break; } Entities.editEntity(leftController.entityID, { @@ -628,43 +658,56 @@ var glowedEntityID = { id: -1, isKnownID: false }; // In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already // exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that // added it. +var ROOT_MENU = "Edit"; +var ITEM_BEFORE = "Physics"; +var MENU_SEPARATOR = "Models"; +var EDIT_PROPERTIES = "Edit Properties..."; +var INTERSECTION_STATS = "Print Intersection Stats"; +var DELETE = "Delete"; +var LARGE_MODELS = "Allow Selecting of Large Models"; +var SMALL_MODELS = "Allow Selecting of Small Models"; + var LIGHTS = "Allow Selecting of Lights"; + var modelMenuAddedDelete = false; var originalLightsArePickable = Entities.getLightsArePickable(); function setupModelMenus() { print("setupModelMenus()"); // adj our menuitems - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Edit Properties...", - shortcutKeyEvent: { text: "`" }, afterItem: "Models" }); - if (!Menu.menuItemExists("Edit", "Delete")) { + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: MENU_SEPARATOR, isSeparator: true, beforeItem: ITEM_BEFORE }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: EDIT_PROPERTIES, + shortcutKeyEvent: { text: "`" }, afterItem: MENU_SEPARATOR }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: INTERSECTION_STATS, afterItem: MENU_SEPARATOR }); + if (!Menu.menuItemExists(ROOT_MENU, DELETE)) { print("no delete... adding ours"); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", - shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: DELETE, + shortcutKeyEvent: { text: "backspace" }, afterItem: MENU_SEPARATOR }); modelMenuAddedDelete = true; } else { print("delete exists... don't add ours"); } - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L", - afterItem: "Paste Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S", - afterItem: "Allow Selecting of Large Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L", - afterItem: "Allow Selecting of Small Models", isCheckable: true }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: LARGE_MODELS, shortcutKey: "CTRL+META+L", + afterItem: DELETE, isCheckable: true }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: SMALL_MODELS, shortcutKey: "CTRL+META+S", + afterItem: LARGE_MODELS, isCheckable: true }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: LIGHTS, shortcutKey: "CTRL+SHIFT+META+L", + afterItem: SMALL_MODELS, isCheckable: true }); Entities.setLightsArePickable(false); } function cleanupModelMenus() { - Menu.removeMenuItem("Edit", "Edit Properties..."); + Menu.removeSeparator(ROOT_MENU, MENU_SEPARATOR); + Menu.removeMenuItem(ROOT_MENU, EDIT_PROPERTIES); + Menu.removeMenuItem(ROOT_MENU, INTERSECTION_STATS); if (modelMenuAddedDelete) { // delete our menuitems - Menu.removeMenuItem("Edit", "Delete"); + Menu.removeMenuItem(ROOT_MENU, DELETE); } - Menu.removeMenuItem("Edit", "Allow Selecting of Large Models"); - Menu.removeMenuItem("Edit", "Allow Selecting of Small Models"); - Menu.removeMenuItem("Edit", "Allow Selecting of Lights"); + Menu.removeMenuItem(ROOT_MENU, LARGE_MODELS); + Menu.removeMenuItem(ROOT_MENU, SMALL_MODELS); + Menu.removeMenuItem(ROOT_MENU, LIGHTS); } @@ -688,13 +731,13 @@ function showPropertiesForm(editModelID) { Menu.menuItemEvent.connect(function (menuItem) { print("menuItemEvent() in JS... menuItem=" + menuItem); - if (menuItem == "Allow Selecting of Small Models") { - allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); - } else if (menuItem == "Allow Selecting of Large Models") { - allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models"); - } else if (menuItem == "Allow Selecting of Lights") { - Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights")); - } else if (menuItem == "Delete") { + if (menuItem == SMALL_MODELS) { + allowSmallModels = Menu.isOptionChecked(SMALL_MODELS); + } else if (menuItem == LARGE_MODELS) { + allowLargeModels = Menu.isOptionChecked(LARGE_MODELS); + } else if (menuItem == LIGHTS) { + Entities.setLightsArePickable(Menu.isOptionChecked(LIGHTS)); + } else if (menuItem == DELETE) { if (leftController.grabbing) { print(" Delete Entity.... leftController.entityID="+ leftController.entityID); Entities.deleteEntity(leftController.entityID); @@ -712,7 +755,7 @@ Menu.menuItemEvent.connect(function (menuItem) { } else { print(" Delete Entity.... not holding..."); } - } else if (menuItem == "Edit Properties...") { + } else if (menuItem == EDIT_PROPERTIES) { editModelID = -1; if (leftController.grabbing) { print(" Edit Properties.... leftController.entityID="+ leftController.entityID); @@ -727,16 +770,18 @@ Menu.menuItemEvent.connect(function (menuItem) { print(" Edit Properties.... about to edit properties..."); showPropertiesForm(editModelID); } + } else if (menuItem == INTERSECTION_STATS) { + printIntersectionsStats(); } }); Controller.keyReleaseEvent.connect(function (event) { // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items if (event.text == "`") { - handeMenuEvent("Edit Properties..."); + handeMenuEvent(EDIT_PROPERTIES); } if (event.text == "BACKSPACE") { - handeMenuEvent("Delete"); + handeMenuEvent(DELETE); } }); diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js new file mode 100644 index 0000000000..85b025e4cd --- /dev/null +++ b/examples/controllers/hydra/paddleBall.js @@ -0,0 +1,172 @@ +// PaddleBall.js +// +// Created by Philip Rosedale on January 21, 2015 +// Copyright 2014 High Fidelity, Inc. +// +// Move your hand with the hydra controller, and hit the ball with the paddle. +// Click 'X' button to turn off this script. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var BALL_SIZE = 0.08; +var PADDLE_SIZE = 0.20; +var PADDLE_THICKNESS = 0.06; +var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; +var BALL_COLOR = { red: 255, green: 0, blue: 0 }; +var LINE_COLOR = { red: 255, green: 255, blue: 0 }; +var PADDLE_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; +var GRAVITY = 0.0; +var SPRING_FORCE = 15.0; +var lastSoundTime = 0; +var gameOn = false; +var leftHanded = false; +var controllerID; + +if (leftHanded) { + controllerID = 1; +} else { + controllerID = 3; +} + + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"); + +var screenSize = Controller.getViewportDimensions(); +var offButton = Overlays.addOverlay("image", { + x: screenSize.x - 48, + y: 96, + width: 32, + height: 32, + imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + +var ball, paddle, paddleModel, line; + +function createEntities() { + ball = Entities.addEntity( + { type: "Sphere", + position: Controller.getSpatialControlPosition(controllerID), + dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, + color: BALL_COLOR, + gravity: { x: 0, y: GRAVITY, z: 0 }, + ignoreCollisions: false, + damping: 0.50, + collisionsWillMove: true }); + + paddle = Entities.addEntity( + { type: "Box", + position: Controller.getSpatialControlPosition(controllerID), + dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, + color: PADDLE_COLOR, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + damping: 0.10, + visible: false, + rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), + collisionsWillMove: false }); + + modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; + paddleModel = Entities.addEntity( + { type: "Model", + position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_OFFSET), + dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, + color: PADDLE_COLOR, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: true, + modelURL: modelURL, + damping: 0.10, + rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), + collisionsWillMove: false }); + + line = Overlays.addOverlay("line3d", { + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: LINE_COLOR, + alpha: 1, + visible: true, + lineWidth: 2 }); +} + +function deleteEntities() { + Entities.deleteEntity(ball); + Entities.deleteEntity(paddle); + Entities.deleteEntity(paddleModel); + Overlays.deleteOverlay(line); +} + +function update(deltaTime) { + var palmPosition = Controller.getSpatialControlPosition(controllerID); + var controllerActive = (Vec3.length(palmPosition) > 0); + + if (!gameOn && controllerActive) { + createEntities(); + gameOn = true; + } else if (gameOn && !controllerActive) { + deleteEntities(); + gameOn = false; + } + if (!gameOn || !controllerActive) { + return; + } + + if (!paddle.isKnownID) { + paddle = Entities.identifyEntity(paddle); + } + if (!ball.isKnownID) { + ball = Entities.identifyEntity(ball); + } else { + var props = Entities.getEntityProperties(ball); + var spring = Vec3.subtract(palmPosition, props.position); + var paddleWorldOrientation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)); + var springLength = Vec3.length(spring); + spring = Vec3.normalize(spring); + var ballVelocity = Vec3.sum(props.velocity, Vec3.multiply(springLength * SPRING_FORCE * deltaTime, spring)); + Entities.editEntity(ball, { velocity: ballVelocity }); + Overlays.editOverlay(line, { start: props.position, end: palmPosition }); + Entities.editEntity(paddle, { position: palmPosition, + velocity: Controller.getSpatialControlVelocity(controllerID), + rotation: paddleWorldOrientation }); + Entities.editEntity(paddleModel, { position: Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_OFFSET)), + velocity: Controller.getSpatialControlVelocity(controllerID), + rotation: paddleWorldOrientation }); + } +} + +function entityCollisionWithEntity(entity1, entity2, collision) { + if ((entity1.id == ball.id) || (entity2.id ==ball.id)) { + var props1 = Entities.getEntityProperties(entity1); + var props2 = Entities.getEntityProperties(entity2); + var dVel = Vec3.length(Vec3.subtract(props1.velocity, props2.velocity)); + var currentTime = new Date().getTime(); + var MIN_MSECS_BETWEEN_BOUNCE_SOUNDS = 100; + var MIN_VELOCITY_FOR_SOUND_IMPACT = 0.25; + if ((dVel > MIN_VELOCITY_FOR_SOUND_IMPACT) && (currentTime - lastSoundTime) > MIN_MSECS_BETWEEN_BOUNCE_SOUNDS) { + Audio.playSound(hitSound, { position: props1.position, volume: Math.min(dVel, 1.0) }); + lastSoundTime = new Date().getTime(); + } + } +} + +function mousePressEvent(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + if (clickedOverlay == offButton) { + Script.stop(); + } +} + +function scriptEnding() { + if (gameOn) { + deleteEntities(); + } + Overlays.deleteOverlay(offButton); +} + +Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity); +Controller.mousePressEvent.connect(mousePressEvent); +Script.scriptEnding.connect(scriptEnding); +Script.update.connect(update); diff --git a/examples/controllers/hydra/squeezeHands.js b/examples/controllers/hydra/squeezeHands.js index 2a4756f017..b1e9274905 100644 --- a/examples/controllers/hydra/squeezeHands.js +++ b/examples/controllers/hydra/squeezeHands.js @@ -60,11 +60,9 @@ Script.update.connect(function(deltaTime) { } if ((leftFrame != lastLeftFrame) && leftHandAnimation.length){ - MyAvatar.stopAnimation(leftHandAnimation); MyAvatar.startAnimation(leftHandAnimation, 30.0, 1.0, false, true, leftFrame, leftFrame); } if ((rightFrame != lastRightFrame) && rightHandAnimation.length) { - MyAvatar.stopAnimation(rightHandAnimation); MyAvatar.startAnimation(rightHandAnimation, 30.0, 1.0, false, true, rightFrame, rightFrame); } diff --git a/examples/editEntities.js b/examples/editEntities.js index 9187b624fd..e2c7a6d435 100644 --- a/examples/editEntities.js +++ b/examples/editEntities.js @@ -669,6 +669,10 @@ function mouseClickEvent(event) { print("Model selected: " + foundEntity.id); selectionDisplay.select(selectedEntityID, event); + + cameraManager.focus(selectionManager.worldPosition, + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } } diff --git a/examples/billiards.js b/examples/example/games/billiards.js similarity index 83% rename from examples/billiards.js rename to examples/example/games/billiards.js index 9bc21ff611..d671addbe6 100644 --- a/examples/billiards.js +++ b/examples/example/games/billiards.js @@ -1,6 +1,7 @@ // Pool Table var tableParts = []; var balls = []; +var cueBall; var LENGTH = 2.84; var WIDTH = 1.42; @@ -13,6 +14,8 @@ var HOLE_SIZE = BALL_SIZE; var DROP_HEIGHT = BALL_SIZE * 3.0; var GRAVITY = -9.8; var BALL_GAP = 0.001; +var tableCenter; +var cuePosition; var startStroke = 0; @@ -23,7 +26,7 @@ var reticle = Overlays.addOverlay("image", { y: screenSize.y / 2 - 16, width: 32, height: 32, - imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png", + imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png", color: { red: 255, green: 255, blue: 255}, alpha: 1 }); @@ -102,7 +105,7 @@ function makeBalls(pos) { { red: 128, green: 128, blue: 128}]; // Gray // Object balls - var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z }; + var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z }; for (var row = 1; row <= 5; row++) { ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE); for (var spot = 0; spot < row; spot++) { @@ -113,23 +116,26 @@ function makeBalls(pos) { color: colors[balls.length], gravity: { x: 0, y: GRAVITY, z: 0 }, ignoreCollisions: false, - damping: 0.40, + damping: 0.50, collisionsWillMove: true })); ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE; } ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE; } // Cue Ball - ballPosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z }; - balls.push(Entities.addEntity( + cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z }; + cueBall = Entities.addEntity( { type: "Sphere", - position: ballPosition, + position: cuePosition, dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, color: { red: 255, green: 255, blue: 255 }, gravity: { x: 0, y: GRAVITY, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0 }, + velocity: {x: 0, y: 0, z: 0 }, ignoreCollisions: false, - damping: 0.40, - collisionsWillMove: true })); + damping: 0.50, + collisionsWillMove: true }); + } function shootCue(velocity) { @@ -140,19 +146,23 @@ function shootCue(velocity) { var velocity = Vec3.multiply(forwardVector, velocity); var BULLET_LIFETIME = 3.0; var BULLET_GRAVITY = 0.0; + var SHOOTER_COLOR = { red: 255, green: 0, blue: 0 }; + var SHOOTER_SIZE = BALL_SIZE / 1.5 * SCALE; + bulletID = Entities.addEntity( { type: "Sphere", position: cuePosition, - dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, - color: { red: 255, green: 255, blue: 255 }, + dimensions: { x: SHOOTER_SIZE, y: SHOOTER_SIZE, z: SHOOTER_SIZE }, + color: SHOOTER_COLOR, velocity: velocity, lifetime: BULLET_LIFETIME, gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, damping: 0.10, - density: 1000, + density: 8000, ignoreCollisions: false, collisionsWillMove: true }); + print("Shot, velocity = " + velocity); } function keyReleaseEvent(event) { @@ -185,9 +195,25 @@ function cleanup() { Entities.deleteEntity(balls[i]); } Overlays.deleteOverlay(reticle); + Entities.deleteEntity(cueBall); } -var tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation()))); +function update(deltaTime) { + if (!cueBall.isKnownID) { + cueBall = Entities.identifyEntity(cueBall); + } else { + // Check if cue ball has fallen off table, re-drop if so + var cueProperties = Entities.getEntityProperties(cueBall); + if (cueProperties.position.y < tableCenter.y) { + // Replace the cueball + Entities.editEntity(cueBall, { position: cuePosition } ); + + } + } + +} + +tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation()))); makeTable(tableCenter); makeBalls(tableCenter); @@ -195,3 +221,4 @@ makeBalls(tableCenter); Script.scriptEnding.connect(cleanup); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.update.connect(update); diff --git a/examples/inspect.js b/examples/inspect.js index ff0925db97..d730a7e53f 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -158,6 +158,11 @@ function handleModes() { avatarOrientation.w != MyAvatar.orientation.w)) { newMode = noMode; } + + if (mode == noMode && newMode != noMode && Camera.mode == "independent") { + newMode = noMode; + } + // if leaving noMode if (mode == noMode && newMode != noMode) { saveCameraState(); diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index b9170dc25d..f5095bb149 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -80,7 +80,8 @@ CameraManager = function() { that.lastMousePosition = { x: 0, y: 0 }; that.enable = function() { - if (that.enabled) return; + if (Camera.mode == "independent" || that.enabled) return; + that.enabled = true; that.mode = MODE_INACTIVE; diff --git a/examples/utilities/record/recorder.js b/examples/utilities/record/recorder.js index f3f46adf1a..495a862db1 100644 --- a/examples/utilities/record/recorder.js +++ b/examples/utilities/record/recorder.js @@ -10,7 +10,7 @@ // HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -Script.include("libraries/toolBars.js"); +Script.include("../../libraries/toolBars.js"); var recordingFile = "recording.rec"; diff --git a/examples/utilities/tools/developerMenuItems.js b/examples/utilities/tools/developerMenuItems.js index 3a274c7083..419285eeb9 100644 --- a/examples/utilities/tools/developerMenuItems.js +++ b/examples/utilities/tools/developerMenuItems.js @@ -18,6 +18,10 @@ function setupMenus() { } if (!Menu.menuExists("Developer > Entities")) { Menu.addMenu("Developer > Entities"); + + // NOTE: these menu items aren't currently working. I've temporarily removed them. Will add them back once we + // rewire these to work + /* Menu.addMenuItem({ menuName: "Developer > Entities", menuItemName: "Display Model Bounds", isCheckable: true, isChecked: false }); Menu.addMenuItem({ menuName: "Developer > Entities", menuItemName: "Display Model Triangles", isCheckable: true, isChecked: false }); Menu.addMenuItem({ menuName: "Developer > Entities", menuItemName: "Display Model Element Bounds", isCheckable: true, isChecked: false }); @@ -26,9 +30,26 @@ function setupMenus() { Menu.addMenuItem({ menuName: "Developer > Entities", menuItemName: "Don't Attempt Render Entities as Scene", isCheckable: true, isChecked: false }); Menu.addMenuItem({ menuName: "Developer > Entities", menuItemName: "Don't Do Precision Picking", isCheckable: true, isChecked: false }); Menu.addMenuItem({ menuName: "Developer > Entities", menuItemName: "Disable Light Entities", isCheckable: true, isChecked: false }); + */ + Menu.addMenuItem({ menuName: "Developer > Entities", menuItemName: "Don't send collision updates to server", isCheckable: true, isChecked: false }); } } +Menu.menuItemEvent.connect(function (menuItem) { + print("menuItemEvent() in JS... menuItem=" + menuItem); + + if (menuItem == "Don't send collision updates to server") { + var dontSendUpdates = Menu.isOptionChecked("Don't send collision updates to server"); + print(" dontSendUpdates... checked=" + dontSendUpdates); + Entities.setSendPhysicsUpdates(!dontSendUpdates); + } +}); + +setupMenus(); + +// register our scriptEnding callback +Script.scriptEnding.connect(scriptEnding); + function scriptEnding() { Menu.removeMenu("Developer > Entities"); } diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt index 24e780f9aa..6a8ca5bd9f 100644 --- a/ice-server/CMakeLists.txt +++ b/ice-server/CMakeLists.txt @@ -4,6 +4,6 @@ set(TARGET_NAME ice-server) setup_hifi_project(Network) # link the shared hifi libraries -link_hifi_libraries(networking shared) +link_hifi_libraries(embedded-webserver networking shared) include_dependency_includes() \ No newline at end of file diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index c06bb0fc88..77deb6125b 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -20,14 +20,18 @@ const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000; const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000; +const quint16 ICE_SERVER_MONITORING_PORT = 40110; + IceServer::IceServer(int argc, char* argv[]) : QCoreApplication(argc, argv), _id(QUuid::createUuid()), _serverSocket(), - _activePeers() + _activePeers(), + _httpManager(ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this) { // start the ice-server socket qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT; + qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT; _serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT); // call our process datagrams slot when the UDP socket has packets ready @@ -165,3 +169,17 @@ void IceServer::clearInactivePeers() { } } } + +bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { + // + // We need an HTTP handler in order to monitor the health of the ice server + // The correct functioning of the ICE server will be determined by its HTTP availability, + // + + if (connection->requestOperation() == QNetworkAccessManager::GetOperation) { + if (url.path() == "/status") { + connection->respond(HTTPConnection::StatusCode200, QByteArray::number(_activePeers.size())); + } + } + return true; +} diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index e15bda1211..be6d298e3d 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -12,17 +12,21 @@ #ifndef hifi_IceServer_h #define hifi_IceServer_h -#include -#include -#include +#include +#include +#include #include +#include +#include typedef QHash NetworkPeerHash; -class IceServer : public QCoreApplication { +class IceServer : public QCoreApplication, public HTTPRequestHandler { + Q_OBJECT public: IceServer(int argc, char* argv[]); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); private slots: void processDatagrams(); void clearInactivePeers(); @@ -34,6 +38,7 @@ private: QUdpSocket _serverSocket; NetworkPeerHash _activePeers; QHash > _currentConnections; + HTTPManager _httpManager; }; #endif // hifi_IceServer_h \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0a59ae4805..cda56b3fe4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -33,13 +33,12 @@ #include #include #include -#include #include #include #include #include -#include #include +#include #include #include #include @@ -73,13 +72,16 @@ #include #include #include +#include #include #include #include #include #include "Application.h" +#include "Audio.h" #include "InterfaceVersion.h" +#include "LODManager.h" #include "Menu.h" #include "ModelUploader.h" #include "Util.h" @@ -100,7 +102,6 @@ #include "gpu/Batch.h" #include "gpu/GLBackend.h" - #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" @@ -112,9 +113,16 @@ #include "scripting/WindowScriptingInterface.h" #include "scripting/WebWindowClass.h" +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) +#include "SpeechRecognizer.h" +#endif + #include "ui/DataWebDialog.h" +#include "ui/DialogsManager.h" #include "ui/InfoView.h" +#include "ui/LoginDialog.h" #include "ui/Snapshot.h" +#include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" @@ -127,9 +135,6 @@ static unsigned STARFIELD_SEED = 1; static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored - -const qint64 MAXIMUM_CACHE_SIZE = 10737418240; // 10GB - static QTimer* idleTimer = NULL; const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml"; @@ -137,6 +142,12 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js"; +namespace SettingHandles { + const SettingHandle firstRun("firstRun", true); + const SettingHandle lastScriptLocation("LastScriptLocation"); + const SettingHandle scriptsLocation("scriptsLocation"); +} + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); @@ -153,6 +164,16 @@ bool setupEssentials(int& argc, char** argv) { listenPort = atoi(portStr); } + // read the ApplicationInfo.ini file for Name/Version/Domain information + QSettings::setDefaultFormat(QSettings::IniFormat); + QSettings applicationInfo(PathUtils::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); + // set the associated application properties + applicationInfo.beginGroup("INFO"); + QApplication::setApplicationName(applicationInfo.value("name").toString()); + QApplication::setApplicationVersion(BUILD_VERSION); + QApplication::setOrganizationName(applicationInfo.value("organizationName").toString()); + QApplication::setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); + DependencyManager::registerInheritance(); // Set dependencies @@ -173,6 +194,12 @@ bool setupEssentials(int& argc, char** argv) { auto ddeFaceTracker = DependencyManager::set(); auto modelBlender = DependencyManager::set(); auto audioToolBox = DependencyManager::set(); + auto lodManager = DependencyManager::set(); + auto jsConsole = DependencyManager::set(); + auto dialogsManager = DependencyManager::set(); +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) + auto speechRecognizer = DependencyManager::set(); +#endif return true; } @@ -192,7 +219,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _justStarted(true), _physicsEngine(glm::vec3(0.0f)), _entities(true, this, this), - _entityCollisionSystem(), _entityClipboardRenderer(false, this, this), _entityClipboard(), _viewFrustum(), @@ -224,35 +250,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _isVSyncOn(true), _aboutToQuit(false) { - auto glCanvas = DependencyManager::get(); - auto nodeList = DependencyManager::get(); + _logger = new FileLogger(this); // After setting organization name in order to get correct directory + qInstallMessageHandler(messageHandler); + + QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf"); + _window->setWindowTitle("Interface"); + Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us - - // read the ApplicationInfo.ini file for Name/Version/Domain information - QSettings applicationInfo(PathUtils::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); - - // set the associated application properties - applicationInfo.beginGroup("INFO"); - - setApplicationName(applicationInfo.value("name").toString()); - setApplicationVersion(BUILD_VERSION); - setOrganizationName(applicationInfo.value("organizationName").toString()); - setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); - - _logger = new FileLogger(this); // After setting organization name in order to get correct directory - - QSettings::setDefaultFormat(QSettings::IniFormat); + auto glCanvas = DependencyManager::get(); + auto nodeList = DependencyManager::get(); _myAvatar = _avatarManager.getMyAvatar(); _applicationStartupTime = startup_time; - QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf"); - _window->setWindowTitle("Interface"); - - qInstallMessageHandler(messageHandler); - qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); _bookmarks = new Bookmarks(); // Before setting up the menu @@ -281,6 +293,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto audioIO = DependencyManager::get