From bb1fe8f4390273f945a2d3417e5ce4093ab37baf Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 18 May 2015 16:57:25 -0700 Subject: [PATCH 01/30] cleanup and stability for grabHockey.js script --- examples/example/games/grabHockey.js | 180 +++++++++++++-------------- 1 file changed, 85 insertions(+), 95 deletions(-) diff --git a/examples/example/games/grabHockey.js b/examples/example/games/grabHockey.js index 7e379294ec..5773dfa849 100644 --- a/examples/example/games/grabHockey.js +++ b/examples/example/games/grabHockey.js @@ -10,6 +10,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var adebug = 0; // these are hand-measured bounds of the AirHockey table var fieldMaxOffset = { x: 0.475, @@ -40,13 +41,14 @@ var entityProps; var moveUpDown = false; var CLOSE_ENOUGH = 0.001; var FULL_STRENGTH = 1.0; -var SPRING_RATE = 1.5; +var SPRING_TIMESCALE = 0.05; var DAMPING_RATE = 0.80; var ANGULAR_DAMPING_RATE = 0.40; var SCREEN_TO_METERS = 0.001; var currentPosition, currentVelocity, cameraEntityDistance, currentRotation; var grabHeight; var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition; +var grabOffset; var originalGravity = { x: 0, y: 0, @@ -94,6 +96,20 @@ function nearLinePoint(targetPosition) { return Vec3.sum(handPosition, along); } +function xzPickRayIntersetion(pointOnPlane, mouseX, mouseY) { + var relativePosition = Vec3.subtract(pointOnPlane, Camera.getPosition()); + var pickRay = Camera.computePickRay(mouseX, mouseY); + if (Math.abs(pickRay.direction.y) > 0.001) { + var length = relativePosition.y / pickRay.direction.y; + var pickInersection = Vec3.multiply(pickRay.direction, length); + pickInersection = Vec3.sum(Camera.getPosition(), pickInersection); + return pickInersection; + } + // point and line are more-or-less co-planar: compute closest approach of pickRay and pointOnPlane + var length = Vec3.dot(relativePosition, pickRay.direction); + var pickInersection = Vec3.multiply(pickRay.direction, length); + return pickInersection; +} function mousePressEvent(event) { if (!event.isLeftButton) { @@ -103,19 +119,23 @@ function mousePressEvent(event) { prevMouse.y = event.y; var pickRay = Camera.computePickRay(event.x, event.y); - var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking - if (!intersection.intersects) { + var pickResults = Entities.findRayIntersection(pickRay, true); // accurate picking + if (!pickResults.intersects) { return; } - if (intersection.properties.collisionsWillMove) { - grabbedEntity = intersection.entityID; + if (pickResults.properties.collisionsWillMove) { + grabbedEntity = pickResults.entityID; var props = Entities.getEntityProperties(grabbedEntity) isGrabbing = true; originalGravity = props.gravity; - targetPosition = props.position; + var objectPosition = props.position; currentPosition = props.position; currentVelocity = props.velocity; - updateDropLine(targetPosition); + updateDropLine(objectPosition); + + var pointOnPlane = xzPickRayIntersetion(objectPosition, event.x, event.y); + grabOffset = Vec3.subtract(pointOnPlane, objectPosition); + targetPosition = objectPosition; // remember the height of the object when first grabbed // we'll try to maintain this height during the rest of this grab @@ -223,53 +243,48 @@ function mouseMoveEvent(event) { axisAngle = Quat.axis(dQ); angularVelocity = Vec3.multiply((theta / dT), axisAngle); } else { - var relativePosition = Vec3.subtract(currentPosition, Camera.getPosition()); - if (relativePosition.y < 0) { - // grabee is below camera, so movement is valid - // compute intersectionPoint where mouse ray hits grabee's current x-z plane - var pickRay = Camera.computePickRay(event.x, event.y); - var mousePosition = pickRay.direction; - var length = relativePosition.y / mousePosition.y; - mousePosition = Vec3.multiply(mousePosition, length); - mousePosition = Vec3.sum(Camera.getPosition(), mousePosition); + var pointOnPlane = xzPickRayIntersetion(currentPosition, event.x, event.y); + pointOnPlane = Vec3.subtract(pointOnPlane, grabOffset); - // translate mousePosition into local-frame - mousePosition = Vec3.subtract(mousePosition, tablePosition); + // translate pointOnPlane into local-frame + pointOnPlane = Vec3.subtract(pointOnPlane, tablePosition); - // clamp local mousePosition to table field - if (mousePosition.x > fieldMaxOffset.x) { - mousePosition.x = fieldMaxOffset.x; - } else if (mousePosition.x < fieldMinOffset.x) { - mousePosition.x = fieldMinOffset.x; - } - if (mousePosition.z > fieldMaxOffset.z) { - mousePosition.z = fieldMaxOffset.z; - } else if (mousePosition.z < fieldMinOffset.z) { - mousePosition.z = fieldMinOffset.z; - } - - // clamp to rotated square (for cut corners) - var rotation = Quat.angleAxis(45, { x:0, y:1, z:0 }); - mousePosition = Vec3.multiplyQbyV(rotation, mousePosition); - if (mousePosition.x > halfCornerBoxWidth) { - mousePosition.x = halfCornerBoxWidth; - } else if (mousePosition.x < -halfCornerBoxWidth) { - mousePosition.x = -halfCornerBoxWidth; - } - if (mousePosition.z > halfCornerBoxWidth) { - mousePosition.z = halfCornerBoxWidth; - } else if (mousePosition.z < -halfCornerBoxWidth) { - mousePosition.z = -halfCornerBoxWidth; - } - // rotate back into local frame - rotation.y = -rotation.y; - mousePosition = Vec3.multiplyQbyV(rotation, mousePosition); - // translate into world-frame - mousePosition = Vec3.sum(tablePosition, mousePosition); - - mousePosition.y = grabHeight; - targetPosition = mousePosition; + // clamp local pointOnPlane to table field + if (pointOnPlane.x > fieldMaxOffset.x) { + pointOnPlane.x = fieldMaxOffset.x; + } else if (pointOnPlane.x < fieldMinOffset.x) { + pointOnPlane.x = fieldMinOffset.x; } + if (pointOnPlane.z > fieldMaxOffset.z) { + pointOnPlane.z = fieldMaxOffset.z; + } else if (pointOnPlane.z < fieldMinOffset.z) { + pointOnPlane.z = fieldMinOffset.z; + } + + // clamp to rotated square (for cut corners) + var rotation = Quat.angleAxis(45, { x:0, y:1, z:0 }); + pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane); + if (pointOnPlane.x > halfCornerBoxWidth) { + pointOnPlane.x = halfCornerBoxWidth; + } else if (pointOnPlane.x < -halfCornerBoxWidth) { + pointOnPlane.x = -halfCornerBoxWidth; + } + if (pointOnPlane.z > halfCornerBoxWidth) { + pointOnPlane.z = halfCornerBoxWidth; + } else if (pointOnPlane.z < -halfCornerBoxWidth) { + pointOnPlane.z = -halfCornerBoxWidth; + } + // rotate back into local frame + rotation.y = -rotation.y; + pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane); + + // translate into world-frame + pointOnPlane = Vec3.sum(tablePosition, pointOnPlane); + + // clamp to gragHeight + pointOnPlane.y = grabHeight; + + targetPosition = pointOnPlane; } } prevMouse.x = event.x; @@ -297,57 +312,32 @@ function keyPressEvent(event) { function update(deltaTime) { dT = deltaTime; if (isGrabbing) { - entityProps = Entities.getEntityProperties(grabbedEntity); currentPosition = entityProps.position; - currentVelocity = entityProps.velocity; - currentRotation = entityProps.rotation; - - var dPosition = Vec3.subtract(targetPosition, currentPosition); - - distanceToTarget = Vec3.length(dPosition); - if (distanceToTarget > CLOSE_ENOUGH) { - // compute current velocity in the direction we want to move - velocityTowardTarget = Vec3.dot(currentVelocity, Vec3.normalize(dPosition)); - velocityTowardTarget = Vec3.multiply(Vec3.normalize(dPosition), velocityTowardTarget); - // compute the speed we would like to be going toward the target position - - desiredVelocity = Vec3.multiply(dPosition, (1.0 / deltaTime) * SPRING_RATE); - // compute how much we want to add to the existing velocity - addedVelocity = Vec3.subtract(desiredVelocity, velocityTowardTarget); - // If target is too far, roll off the force as inverse square of distance - if (distanceToTarget / cameraEntityDistance > FULL_STRENGTH) { - addedVelocity = Vec3.multiply(addedVelocity, Math.pow(FULL_STRENGTH / distanceToTarget, 2.0)); - } - newVelocity = Vec3.sum(currentVelocity, addedVelocity); - // Add Damping - newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE)); - // Update entity - } else { - newVelocity = { - x: 0, - y: 0, - z: 0 - }; - } if (shouldRotate) { angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE)); + Entities.editEntity(grabbedEntity, { angularVelocity: angularVelocity, }); } else { - angularVelocity = entityProps.angularVelocity; + var dPosition = Vec3.subtract(targetPosition, currentPosition); + var delta = Vec3.length(dPosition); + if (delta > CLOSE_ENOUGH) { + var MAX_POSITION_DELTA = 0.50; + if (delta > MAX_POSITION_DELTA) { + dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta); + } + // desired speed is proportional to displacement by the inverse of timescale + // (for critically damped motion) + newVelocity = Vec3.multiply(dPosition, 1.0 / SPRING_TIMESCALE); + } else { + newVelocity = { + x: 0, + y: 0, + z: 0 + }; + } + Entities.editEntity(grabbedEntity, { velocity: newVelocity, }); } - // enforce that grabee's rotation is only about y-axis while being grabbed - currentRotation.x = 0; - currentRotation.z = 0; - currentRotation.y = Math.sqrt(1.0 - currentRotation.w * currentRotation.w); - // Hrm... slamming the currentRotation doesn't seem to work - - Entities.editEntity(grabbedEntity, { - position: currentPosition, - rotation: currentRotation, - velocity: newVelocity, - angularVelocity: angularVelocity - }); updateDropLine(targetPosition); } } From b60907570a5db887bf94a5876399dcfd5c3503d6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 18 May 2015 22:32:20 -0700 Subject: [PATCH 02/30] safe to use grabHockey.js to grab misc objects --- examples/example/games/grabHockey.js | 149 +++++++++++++++++++-------- 1 file changed, 104 insertions(+), 45 deletions(-) diff --git a/examples/example/games/grabHockey.js b/examples/example/games/grabHockey.js index 5773dfa849..153016e639 100644 --- a/examples/example/games/grabHockey.js +++ b/examples/example/games/grabHockey.js @@ -10,7 +10,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var adebug = 0; // these are hand-measured bounds of the AirHockey table var fieldMaxOffset = { x: 0.475, @@ -30,12 +29,14 @@ var tablePosition = { z: 0 } var isGrabbing = false; +var isGrabbingPaddle = false; var grabbedEntity = null; var prevMouse = {}; var deltaMouse = { z: 0 } +var MAX_GRAB_DISTANCE = 100; var TABLE_SEARCH_RANGE = 10; var entityProps; var moveUpDown = false; @@ -47,6 +48,9 @@ var ANGULAR_DAMPING_RATE = 0.40; var SCREEN_TO_METERS = 0.001; var currentPosition, currentVelocity, cameraEntityDistance, currentRotation; var grabHeight; +var initialVerticalGrabPosition; +var MAX_VERTICAL_ANGLE = Math.PI / 3; +var MIN_VERTICAL_ANGLE = - MAX_VERTICAL_ANGLE; var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition; var grabOffset; var originalGravity = { @@ -97,20 +101,50 @@ function nearLinePoint(targetPosition) { } function xzPickRayIntersetion(pointOnPlane, mouseX, mouseY) { - var relativePosition = Vec3.subtract(pointOnPlane, Camera.getPosition()); + var relativePoint = Vec3.subtract(pointOnPlane, Camera.getPosition()); var pickRay = Camera.computePickRay(mouseX, mouseY); if (Math.abs(pickRay.direction.y) > 0.001) { - var length = relativePosition.y / pickRay.direction.y; - var pickInersection = Vec3.multiply(pickRay.direction, length); + var distance = relativePoint.y / pickRay.direction.y; + var pickInersection = Vec3.multiply(pickRay.direction, distance); pickInersection = Vec3.sum(Camera.getPosition(), pickInersection); return pickInersection; } // point and line are more-or-less co-planar: compute closest approach of pickRay and pointOnPlane - var length = Vec3.dot(relativePosition, pickRay.direction); - var pickInersection = Vec3.multiply(pickRay.direction, length); + var distance = Vec3.dot(relativePoint, pickRay.direction); + var pickInersection = Vec3.multiply(pickRay.direction, distance); return pickInersection; } +function forwardPickRayIntersection(pointOnPlane, mouseX, mouseY) { + var relativePoint = Vec3.subtract(pointOnPlane, Camera.getPosition()); + var pickRay = Camera.computePickRay(mouseX, mouseY); + var planeNormal = Quat.getFront(Camera.getOrientation()); + var distance = Vec3.dot(planeNormal, relativePoint); + var rayDistance = Vec3.dot(planeNormal, pickRay.direction); + var pickIntersection = Vec3.multiply(pickRay.direction, distance / rayDistance); + pickIntersection = Vec3.sum(pickIntersection, Camera.getPosition()) + return pickIntersection; +} + +/* +function yCylinderPickRayIntersection(grabRadius, mouseX, mouseY) { + var pickRay = Camera.computePickRay(mouseX, mouseY); + var angle = Math.asin(pickRay.direction.y); + if (angle > MAX_VERTICAL_ANGLE) { + angle = MAX_VERTICAL_ANGLE; + } else if (angle < MIN_VERTICAL_ANGLE) { + angle = MIN_VERTICAL_ANGLE; + } + var horizontalNormal = pickRay.direction; + horizontalNormal.y = 0; + horizontalNormal = Vec3.normalize(horizontalNormal); + var pickIntersection = Vec3.multiply(horizontalNormal, grabRadius); + pickIntersection.y = grabRadius * Math.tan(angle); + pickIntersection = Vec3.sum(pickIntersection, Camera.getPosition()) + return pickIntersection; +} +*/ + function mousePressEvent(event) { if (!event.isLeftButton) { return; @@ -126,10 +160,17 @@ function mousePressEvent(event) { if (pickResults.properties.collisionsWillMove) { grabbedEntity = pickResults.entityID; var props = Entities.getEntityProperties(grabbedEntity) - isGrabbing = true; originalGravity = props.gravity; var objectPosition = props.position; currentPosition = props.position; + if (Vec3.distance(currentPosition, Camera.getPosition()) > MAX_GRAB_DISTANCE) { + // don't allow grabs of things far away + return; + } + + isGrabbing = true; + isGrabbingPaddle = (props.name == "air-hockey-paddle-23j4h1jh82jsjfw91jf232n2k"); + currentVelocity = props.velocity; updateDropLine(objectPosition); @@ -140,6 +181,7 @@ function mousePressEvent(event) { // remember the height of the object when first grabbed // we'll try to maintain this height during the rest of this grab grabHeight = currentPosition.y; + initialVerticalGrabPosition = currentPosition; Entities.editEntity(grabbedEntity, { gravity: { @@ -243,48 +285,65 @@ function mouseMoveEvent(event) { axisAngle = Quat.axis(dQ); angularVelocity = Vec3.multiply((theta / dT), axisAngle); } else { - var pointOnPlane = xzPickRayIntersetion(currentPosition, event.x, event.y); - pointOnPlane = Vec3.subtract(pointOnPlane, grabOffset); + if (moveUpDown) { + targetPosition = forwardPickRayIntersection(currentPosition, event.x, event.y); + grabHeight = targetPosition.y; + } else { + var pointOnPlane = xzPickRayIntersetion(currentPosition, event.x, event.y); + pointOnPlane = Vec3.subtract(pointOnPlane, grabOffset); - // translate pointOnPlane into local-frame - pointOnPlane = Vec3.subtract(pointOnPlane, tablePosition); + if (isGrabbingPaddle) { + // translate pointOnPlane into local-frame + pointOnPlane = Vec3.subtract(pointOnPlane, tablePosition); - // clamp local pointOnPlane to table field - if (pointOnPlane.x > fieldMaxOffset.x) { - pointOnPlane.x = fieldMaxOffset.x; - } else if (pointOnPlane.x < fieldMinOffset.x) { - pointOnPlane.x = fieldMinOffset.x; - } - if (pointOnPlane.z > fieldMaxOffset.z) { - pointOnPlane.z = fieldMaxOffset.z; - } else if (pointOnPlane.z < fieldMinOffset.z) { - pointOnPlane.z = fieldMinOffset.z; + // clamp local pointOnPlane to table field + if (pointOnPlane.x > fieldMaxOffset.x) { + pointOnPlane.x = fieldMaxOffset.x; + } else if (pointOnPlane.x < fieldMinOffset.x) { + pointOnPlane.x = fieldMinOffset.x; + } + if (pointOnPlane.z > fieldMaxOffset.z) { + pointOnPlane.z = fieldMaxOffset.z; + } else if (pointOnPlane.z < fieldMinOffset.z) { + pointOnPlane.z = fieldMinOffset.z; + } + + // clamp to rotated square (for cut corners) + var rotation = Quat.angleAxis(45, { x:0, y:1, z:0 }); + pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane); + if (pointOnPlane.x > halfCornerBoxWidth) { + pointOnPlane.x = halfCornerBoxWidth; + } else if (pointOnPlane.x < -halfCornerBoxWidth) { + pointOnPlane.x = -halfCornerBoxWidth; + } + if (pointOnPlane.z > halfCornerBoxWidth) { + pointOnPlane.z = halfCornerBoxWidth; + } else if (pointOnPlane.z < -halfCornerBoxWidth) { + pointOnPlane.z = -halfCornerBoxWidth; + } + // rotate back into local frame + rotation.y = -rotation.y; + pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane); + + // translate into world-frame + pointOnPlane = Vec3.sum(tablePosition, pointOnPlane); + } else { + // we're grabbing a non-paddle object + + // clamp distance + var relativePosition = Vec3.subtract(pointOnPlane, Camera.getPosition()); + var length = Vec3.length(relativePosition); + if (length > MAX_GRAB_DISTANCE) { + relativePosition = Vec3.multiply(relativePosition, MAX_GRAB_DISTANCE / length); + pointOnPlane = Vec3.sum(relativePosition, Camera.getPosition()); + } + } + // clamp to grabHeight + pointOnPlane.y = grabHeight; + targetPosition = pointOnPlane; + initialVerticalGrabPosition = targetPosition; } - // clamp to rotated square (for cut corners) - var rotation = Quat.angleAxis(45, { x:0, y:1, z:0 }); - pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane); - if (pointOnPlane.x > halfCornerBoxWidth) { - pointOnPlane.x = halfCornerBoxWidth; - } else if (pointOnPlane.x < -halfCornerBoxWidth) { - pointOnPlane.x = -halfCornerBoxWidth; - } - if (pointOnPlane.z > halfCornerBoxWidth) { - pointOnPlane.z = halfCornerBoxWidth; - } else if (pointOnPlane.z < -halfCornerBoxWidth) { - pointOnPlane.z = -halfCornerBoxWidth; - } - // rotate back into local frame - rotation.y = -rotation.y; - pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane); - - // translate into world-frame - pointOnPlane = Vec3.sum(tablePosition, pointOnPlane); - - // clamp to gragHeight - pointOnPlane.y = grabHeight; - - targetPosition = pointOnPlane; } } prevMouse.x = event.x; From fbd1b7e8b0d8c8c981a0c56a681025ef51604b7d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 18 May 2015 22:49:23 -0700 Subject: [PATCH 03/30] grabHockey.js can also rotate objects again --- examples/example/games/grabHockey.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/examples/example/games/grabHockey.js b/examples/example/games/grabHockey.js index 153016e639..57333e0d1e 100644 --- a/examples/example/games/grabHockey.js +++ b/examples/example/games/grabHockey.js @@ -31,7 +31,7 @@ var tablePosition = { var isGrabbing = false; var isGrabbingPaddle = false; var grabbedEntity = null; -var prevMouse = {}; +var prevMouse = {x: 0, y: 0}; var deltaMouse = { z: 0 } @@ -43,6 +43,7 @@ var moveUpDown = false; var CLOSE_ENOUGH = 0.001; var FULL_STRENGTH = 1.0; var SPRING_TIMESCALE = 0.05; +var ANGULAR_SPRING_TIMESCALE = 0.03; var DAMPING_RATE = 0.80; var ANGULAR_DAMPING_RATE = 0.40; var SCREEN_TO_METERS = 0.001; @@ -59,7 +60,7 @@ var originalGravity = { z: 0 }; var shouldRotate = false; -var dQ, theta, axisAngle, dT; +var dQ, dT; var angularVelocity = { x: 0, y: 0, @@ -266,24 +267,20 @@ function mouseMoveEvent(event) { } if (shouldRotate) { + targetPosition = currentPosition; deltaMouse.x = event.x - prevMouse.x; - if (!moveUpDown) { - deltaMouse.z = event.y - prevMouse.y; - deltaMouse.y = 0; - } else { - deltaMouse.y = (event.y - prevMouse.y) * -1; - deltaMouse.z = 0; - } + deltaMouse.z = event.y - prevMouse.y; + deltaMouse.y = 0; + var transformedDeltaMouse = { x: deltaMouse.z, y: deltaMouse.x, z: deltaMouse.y }; - transformedDeltaMouse = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), transformedDeltaMouse); dQ = Quat.fromVec3Degrees(transformedDeltaMouse); - theta = 2 * Math.acos(dQ.w); - axisAngle = Quat.axis(dQ); - angularVelocity = Vec3.multiply((theta / dT), axisAngle); + var theta = 2 * Math.acos(dQ.w); + var axis = Quat.axis(dQ); + angularVelocity = Vec3.multiply((theta / ANGULAR_SPRING_TIMESCALE), axis); } else { if (moveUpDown) { targetPosition = forwardPickRayIntersection(currentPosition, event.x, event.y); From d486899aadd2558458a82a431bd9b420098812e3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 18 May 2015 22:54:00 -0700 Subject: [PATCH 04/30] remove unused experimental grab placement --- examples/example/games/grabHockey.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/examples/example/games/grabHockey.js b/examples/example/games/grabHockey.js index 57333e0d1e..afce60c358 100644 --- a/examples/example/games/grabHockey.js +++ b/examples/example/games/grabHockey.js @@ -127,25 +127,6 @@ function forwardPickRayIntersection(pointOnPlane, mouseX, mouseY) { return pickIntersection; } -/* -function yCylinderPickRayIntersection(grabRadius, mouseX, mouseY) { - var pickRay = Camera.computePickRay(mouseX, mouseY); - var angle = Math.asin(pickRay.direction.y); - if (angle > MAX_VERTICAL_ANGLE) { - angle = MAX_VERTICAL_ANGLE; - } else if (angle < MIN_VERTICAL_ANGLE) { - angle = MIN_VERTICAL_ANGLE; - } - var horizontalNormal = pickRay.direction; - horizontalNormal.y = 0; - horizontalNormal = Vec3.normalize(horizontalNormal); - var pickIntersection = Vec3.multiply(horizontalNormal, grabRadius); - pickIntersection.y = grabRadius * Math.tan(angle); - pickIntersection = Vec3.sum(pickIntersection, Camera.getPosition()) - return pickIntersection; -} -*/ - function mousePressEvent(event) { if (!event.isLeftButton) { return; From 139ebca258e6cb9a05792b6fd12c0fea67370727 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 18 May 2015 23:09:08 -0700 Subject: [PATCH 05/30] less bad behavior when dragging across horizon --- examples/example/games/grabHockey.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/example/games/grabHockey.js b/examples/example/games/grabHockey.js index afce60c358..fc21caee1d 100644 --- a/examples/example/games/grabHockey.js +++ b/examples/example/games/grabHockey.js @@ -106,6 +106,9 @@ function xzPickRayIntersetion(pointOnPlane, mouseX, mouseY) { var pickRay = Camera.computePickRay(mouseX, mouseY); if (Math.abs(pickRay.direction.y) > 0.001) { var distance = relativePoint.y / pickRay.direction.y; + if (distance < 0.001) { + return pointOnPlane; + } var pickInersection = Vec3.multiply(pickRay.direction, distance); pickInersection = Vec3.sum(Camera.getPosition(), pickInersection); return pickInersection; @@ -238,7 +241,6 @@ function mouseReleaseEvent() { } } -// new mouseMoveEvent function mouseMoveEvent(event) { if (isGrabbing) { // see if something added/restored gravity @@ -321,7 +323,6 @@ function mouseMoveEvent(event) { targetPosition = pointOnPlane; initialVerticalGrabPosition = targetPosition; } - } } prevMouse.x = event.x; From 09085f1cd93318d402871b6d2d435723a47f98f9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 May 2015 18:42:38 -0700 Subject: [PATCH 06/30] Working towards display plugins --- CMakeLists.txt | 6 +- cmake/externals/boostconfig/CMakeLists.txt | 18 +++++ cmake/externals/oglplus/CMakeLists.txt | 18 +++++ cmake/externals/openvr/CMakeLists.txt | 4 +- cmake/modules/FindBoostConfig.cmake | 24 ++++++ cmake/modules/FindOGLPLUS.cmake | 24 ++++++ libraries/octree/src/ViewFrustum.cpp | 9 ++- libraries/octree/src/ViewFrustum.h | 10 +-- libraries/render-utils/src/GlWindow.cpp | 78 +++++++++++++++++++ libraries/render-utils/src/GlWindow.h | 34 ++++++++ .../render-utils/src/OffscreenGlCanvas.cpp | 22 +++++- .../render-utils/src/OffscreenGlCanvas.h | 8 ++ .../render-utils/src/OffscreenGlContext.cpp | 43 ---------- .../render-utils/src/OffscreenGlContext.h | 33 -------- 14 files changed, 243 insertions(+), 88 deletions(-) create mode 100644 cmake/externals/boostconfig/CMakeLists.txt create mode 100644 cmake/externals/oglplus/CMakeLists.txt create mode 100644 cmake/modules/FindBoostConfig.cmake create mode 100644 cmake/modules/FindOGLPLUS.cmake create mode 100644 libraries/render-utils/src/GlWindow.cpp create mode 100644 libraries/render-utils/src/GlWindow.h delete mode 100644 libraries/render-utils/src/OffscreenGlContext.cpp delete mode 100644 libraries/render-utils/src/OffscreenGlContext.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 347341efa0..f1f83f063a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,8 +177,12 @@ option(GET_GVERB "Get Gverb library automatically as external project" 1) option(GET_SOXR "Get Soxr library automatically as external project" 1) option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1) option(GET_LIBOVR "Get LibOVR library automatically as external project" 1) -option(USE_NSIGHT "Attempt to find the nSight libraries" 1) option(GET_VHACD "Get V-HACD library automatically as external project" 1) +option(GET_OPENVR "Get OpenVR library automatically as external project" 1) +option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1) +option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1) + +option(USE_NSIGHT "Attempt to find the nSight libraries" 1) if (WIN32) option(GET_GLEW "Get GLEW library automatically as external project" 1) diff --git a/cmake/externals/boostconfig/CMakeLists.txt b/cmake/externals/boostconfig/CMakeLists.txt new file mode 100644 index 0000000000..8785e0d7c7 --- /dev/null +++ b/cmake/externals/boostconfig/CMakeLists.txt @@ -0,0 +1,18 @@ +set(EXTERNAL_NAME boostconfig) +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://github.com/boostorg/config/archive/boost-1.58.0.zip + URL_MD5 42fa673bae2b7645a22736445e80eb8d + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 +) + +ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL) + diff --git a/cmake/externals/oglplus/CMakeLists.txt b/cmake/externals/oglplus/CMakeLists.txt new file mode 100644 index 0000000000..1413edce34 --- /dev/null +++ b/cmake/externals/oglplus/CMakeLists.txt @@ -0,0 +1,18 @@ +set(EXTERNAL_NAME oglplus) +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://softlayer-dal.dl.sourceforge.net/project/oglplus/oglplus-0.61.x/oglplus-0.61.0.zip + URL_MD5 bb55038c36c660d2b6c7be380414fa60 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 +) + +ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include ${SOURCE_DIR}/implement CACHE TYPE INTERNAL) + diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt index b5cd477c02..826c3dbb13 100644 --- a/cmake/externals/openvr/CMakeLists.txt +++ b/cmake/externals/openvr/CMakeLists.txt @@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://github.com/ValveSoftware/openvr/archive/0.9.0.zip - URL_MD5 4fbde7759f604aaa68b9c40d628cc34a + URL https://github.com/ValveSoftware/openvr/archive/0.9.1.zip + URL_MD5 f986f5a6815e9454c53c5bf58ce02fdc CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/modules/FindBoostConfig.cmake b/cmake/modules/FindBoostConfig.cmake new file mode 100644 index 0000000000..90682d2425 --- /dev/null +++ b/cmake/modules/FindBoostConfig.cmake @@ -0,0 +1,24 @@ +# +# Try to find BOOSTCONFIG include path. +# Once done this will define +# +# BOOSTCONFIG_INCLUDE_DIRS +# +# Created by Bradley Austin Davis on 2015/05/22 +# Copyright 2015 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + +# setup hints for BOOSTCONFIG search +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("BOOSTCONFIG") + +# locate header +find_path(BOOSTCONFIG_INCLUDE_DIRS "boost/config.hpp" HINTS ${BOOSTCONFIG_SEARCH_DIRS}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(BOOSTCONFIG DEFAULT_MSG BOOSTCONFIG_INCLUDE_DIRS) + +mark_as_advanced(BOOSTCONFIG_INCLUDE_DIRS BOOSTCONFIG_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindOGLPLUS.cmake b/cmake/modules/FindOGLPLUS.cmake new file mode 100644 index 0000000000..6ba883ee2c --- /dev/null +++ b/cmake/modules/FindOGLPLUS.cmake @@ -0,0 +1,24 @@ +# +# Try to find OGLPLUS include path. +# Once done this will define +# +# OGLPLUS_INCLUDE_DIRS +# +# Created by Bradley Austin Davis on 2015/05/22 +# Copyright 2015 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + +# setup hints for OGLPLUS search +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("oglplus") + +# locate header +find_path(OGLPLUS_INCLUDE_DIRS "oglplus/fwd.hpp" HINTS ${OGLPLUS_SEARCH_DIRS}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OGLPLUS DEFAULT_MSG OGLPLUS_INCLUDE_DIRS) + +mark_as_advanced(OGLPLUS_INCLUDE_DIRS OGLPLUS_SEARCH_DIRS) \ No newline at end of file diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index 2694314b57..80f0023970 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -27,14 +27,17 @@ using namespace std; -ViewFrustum::ViewFrustum() { -} - void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) { _orientation = orientationAsQuaternion; _right = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_RIGHT, 0.0f)); _up = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_UP, 0.0f)); _direction = glm::vec3(orientationAsQuaternion * glm::vec4(IDENTITY_FRONT, 0.0f)); + _view = glm::translate(mat4(), _position) * glm::mat4_cast(_orientation); +} + +void ViewFrustum::setPosition(const glm::vec3& position) { + _position = position; + _view = glm::translate(mat4(), _position) * glm::mat4_cast(_orientation); } // Order cooresponds to the order defined in the BoxVertex enum. diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 0422120e51..43bc1d992e 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -36,11 +36,9 @@ const float DEFAULT_FAR_CLIP = (float)TREE_SCALE; class ViewFrustum { public: - ViewFrustum(); - // setters for camera attributes - void setPosition(const glm::vec3& p) { _position = p; } - void setOrientation(const glm::quat& orientationAsQuaternion); + void setPosition(const glm::vec3& position); + void setOrientation(const glm::quat& orientation); // getters for camera attributes const glm::vec3& getPosition() const { return _position; } @@ -54,7 +52,8 @@ public: void getFocalLength(float focalLength) { _focalLength = focalLength; } // getters for lens attributes - const glm::mat4 getProjection() const { return _projection; }; + const glm::mat4& getProjection() const { return _projection; }; + const glm::mat4& getView() const { return _view; }; float getWidth() const { return _width; } float getHeight() const { return _height; } float getFieldOfView() const { return _fieldOfView; } @@ -120,6 +119,7 @@ private: // camera location/orientation attributes glm::vec3 _position; // the position in world-frame glm::quat _orientation; + glm::mat4 _view; // Lens attributes glm::mat4 _projection; diff --git a/libraries/render-utils/src/GlWindow.cpp b/libraries/render-utils/src/GlWindow.cpp new file mode 100644 index 0000000000..b395140518 --- /dev/null +++ b/libraries/render-utils/src/GlWindow.cpp @@ -0,0 +1,78 @@ +// +// Created by Bradley Austin Davis on 2015/05/21 +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GlWindow.h" + +#include +#include + +static QSurfaceFormat getDefaultFormat() { + QSurfaceFormat format; + // Qt Quick may need a depth and stencil buffer. Always make sure these are available. + format.setDepthBufferSize(16); + format.setStencilBufferSize(8); + format.setVersion(4, 1); +#ifdef DEBUG + format.setOption(QSurfaceFormat::DebugContext); +#endif + format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile); + return format; +} + +GlWindow::GlWindow(QOpenGLContext * shareContext) : GlWindow(getDefaultFormat(), shareContext) { +} + +GlWindow::GlWindow(const QSurfaceFormat& format, QOpenGLContext * shareContext) { + setSurfaceType(QSurface::OpenGLSurface); + setFormat(format); + _context = new QOpenGLContext; + _context->setFormat(format); + if (shareContext) { + _context->setShareContext(shareContext); + } + _context->create(); +} + +GlWindow::~GlWindow() { +#ifdef DEBUG + if (_logger) { + makeCurrent(); + delete _logger; + _logger = nullptr; + } +#endif + _context->doneCurrent(); + _context->deleteLater(); + _context = nullptr; +} + + +void GlWindow::makeCurrent() { + _context->makeCurrent(this); +#ifdef DEBUG + if (!_logger) { + _logger = new QOpenGLDebugLogger(this); + if (_logger->initialize()) { + connect(_logger, &QOpenGLDebugLogger::messageLogged, [](const QOpenGLDebugMessage& message) { + qDebug() << message; + }); + _logger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::NotificationSeverity); + _logger->startLogging(QOpenGLDebugLogger::LoggingMode::SynchronousLogging); + } + } +#endif +} + +void GlWindow::doneCurrent() { + _context->doneCurrent(); +} + +void GlWindow::swapBuffers() { + _context->swapBuffers(this); +} + diff --git a/libraries/render-utils/src/GlWindow.h b/libraries/render-utils/src/GlWindow.h new file mode 100644 index 0000000000..69fb09932e --- /dev/null +++ b/libraries/render-utils/src/GlWindow.h @@ -0,0 +1,34 @@ +// +// Created by Bradley Austin Davis on 2015/05/21 +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_GlWindow_h +#define hifi_GlWindow_h + +#include +#include + +class QOpenGLContext; +class QOpenGLDebugLogger; + +class GlWindow : public QWindow { +public: + GlWindow(QOpenGLContext * shareContext = nullptr); + GlWindow(const QSurfaceFormat& format, QOpenGLContext * shareContext = nullptr); + virtual ~GlWindow(); + void makeCurrent(); + void doneCurrent(); + void swapBuffers(); +private: + QOpenGLContext * _context{ nullptr }; +#ifdef DEBUG + QOpenGLDebugLogger * _logger{ nullptr }; +#endif +}; + +#endif \ No newline at end of file diff --git a/libraries/render-utils/src/OffscreenGlCanvas.cpp b/libraries/render-utils/src/OffscreenGlCanvas.cpp index 0e5d4928d8..a3025bc3f6 100644 --- a/libraries/render-utils/src/OffscreenGlCanvas.cpp +++ b/libraries/render-utils/src/OffscreenGlCanvas.cpp @@ -11,6 +11,7 @@ #include "OffscreenGlCanvas.h" +#include OffscreenGlCanvas::OffscreenGlCanvas() { } @@ -27,16 +28,35 @@ void OffscreenGlCanvas::create(QOpenGLContext* sharedContext) { format.setMajorVersion(4); format.setMinorVersion(1); format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile); +#ifdef DEBUG + format.setOption(QSurfaceFormat::DebugContext); +#endif _context.setFormat(format); } _context.create(); _offscreenSurface.setFormat(_context.format()); _offscreenSurface.create(); + } bool OffscreenGlCanvas::makeCurrent() { - return _context.makeCurrent(&_offscreenSurface); + bool result = _context.makeCurrent(&_offscreenSurface); + +#ifdef DEBUG + if (result && !_logger) { + _logger = new QOpenGLDebugLogger(this); + if (_logger->initialize()) { + connect(_logger, &QOpenGLDebugLogger::messageLogged, [](const QOpenGLDebugMessage& message) { + qDebug() << message; + }); + _logger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::NotificationSeverity); + _logger->startLogging(QOpenGLDebugLogger::LoggingMode::SynchronousLogging); + } + } +#endif + + return result; } void OffscreenGlCanvas::doneCurrent() { diff --git a/libraries/render-utils/src/OffscreenGlCanvas.h b/libraries/render-utils/src/OffscreenGlCanvas.h index 5a64a4cf10..c9578f5a7c 100644 --- a/libraries/render-utils/src/OffscreenGlCanvas.h +++ b/libraries/render-utils/src/OffscreenGlCanvas.h @@ -15,16 +15,24 @@ #include #include +class QOpenGLDebugLogger; + class OffscreenGlCanvas : public QObject { public: OffscreenGlCanvas(); void create(QOpenGLContext* sharedContext = nullptr); bool makeCurrent(); void doneCurrent(); + QOpenGLContext* getContext() { + return &_context; + } protected: QOpenGLContext _context; QOffscreenSurface _offscreenSurface; +#ifdef DEBUG + QOpenGLDebugLogger * _logger{ nullptr }; +#endif }; diff --git a/libraries/render-utils/src/OffscreenGlContext.cpp b/libraries/render-utils/src/OffscreenGlContext.cpp deleted file mode 100644 index 96f0a93c3a..0000000000 --- a/libraries/render-utils/src/OffscreenGlContext.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// -// OffscreenGlCanvas.cpp -// interface/src/renderer -// -// Created by Bradley Austin Davis on 2014/04/09. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -#include "OffscreenGlContext.h" - -OffscreenGlContext::OffscreenGlContext() { -} - -void OffscreenGlContext::create(QOpenGLContext * sharedContext) { - QSurfaceFormat format; - format.setDepthBufferSize(16); - format.setStencilBufferSize(8); - format.setMajorVersion(4); - format.setMinorVersion(1); - format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile); - - _context.setFormat(format); - if (nullptr != sharedContext) { - _context.setShareContext(sharedContext); - } - _context.create(); - - _offscreenSurface.setFormat(_context.format()); - _offscreenSurface.create(); -} - -bool OffscreenGlContext::makeCurrent() { - return _context.makeCurrent(&_offscreenSurface); -} - -void OffscreenGlContext::doneCurrent() { - _context.doneCurrent(); -} - diff --git a/libraries/render-utils/src/OffscreenGlContext.h b/libraries/render-utils/src/OffscreenGlContext.h deleted file mode 100644 index c0d22b268f..0000000000 --- a/libraries/render-utils/src/OffscreenGlContext.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// OffscreenGlCanvas.h -// interface/src/renderer -// -// Created by Bradley Austin Davis on 2014/04/09. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#pragma once -#ifndef hifi_OffscreenGlContext_h -#define hifi_OffscreenGlContext_h - -#include -#include - -class OffscreenGlContext : public QObject { -public: - OffscreenGlContext(); - void create(QOpenGLContext * sharedContext = nullptr); - bool makeCurrent(); - void doneCurrent(); - QOpenGLContext * getContext() { - return &_context; - } - -protected: - QOpenGLContext _context; - QOffscreenSurface _offscreenSurface; -}; - -#endif // hifi_OffscreenGlCanvas_h From 7be16da1a8d5affeb3790b4a1a033b067a69120a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 May 2015 14:24:39 -0700 Subject: [PATCH 07/30] compute correct time-of-flight for extrapolation --- libraries/entities/src/EntityItem.cpp | 33 ++++++++++++++++----------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 64196d1c35..4a9b64d1ca 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -480,17 +480,21 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bytesRead += encodedUpdateDelta.size(); // Newer bitstreams will have a last simulated and a last updated value + quint64 lastSimulatedFromBufferAdjusted = now; if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME) { // last simulated is stored as ByteCountCoded delta from lastEdited QByteArray encodedSimulatedDelta = originalDataBuffer.mid(bytesRead); // maximum possible size ByteCountCoded simulatedDeltaCoder = encodedSimulatedDelta; quint64 simulatedDelta = simulatedDeltaCoder; if (overwriteLocalData) { - _lastSimulated = lastEditedFromBufferAdjusted + simulatedDelta; // don't adjust for clock skew since we already did that + lastSimulatedFromBufferAdjusted = lastEditedFromBufferAdjusted + simulatedDelta; // don't adjust for clock skew since we already did that + if (lastSimulatedFromBufferAdjusted > now) { + lastSimulatedFromBufferAdjusted = now; + } #ifdef WANT_DEBUG - qCDebug(entities) << " _lastSimulated:" << debugTime(_lastSimulated, now); qCDebug(entities) << " _lastEdited:" << debugTime(_lastEdited, now); qCDebug(entities) << " lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now); + qCDebug(entities) << " lastSimulatedFromBufferAdjusted:" << debugTime(lastSimulatedFromBufferAdjusted, now); #endif } encodedSimulatedDelta = simulatedDeltaCoder; // determine true length @@ -606,8 +610,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // use our simulation helper routine to get a best estimate of where the entity should be. const float MIN_TIME_SKIP = 0.0f; const float MAX_TIME_SKIP = 1.0f; // in seconds - float skipTimeForward = glm::clamp((float)(now - _lastSimulated) / (float)(USECS_PER_SECOND), - MIN_TIME_SKIP, MAX_TIME_SKIP); + float skipTimeForward = glm::clamp((float)(now - lastSimulatedFromBufferAdjusted) / (float)(USECS_PER_SECOND), + MIN_TIME_SKIP, MAX_TIME_SKIP); if (skipTimeForward > 0.0f) { #ifdef WANT_DEBUG qCDebug(entities) << "skipTimeForward:" << skipTimeForward; @@ -617,19 +621,22 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // we don't want the side effect of flag setting. simulateKinematicMotion(skipTimeForward, false); } - _lastSimulated = now; } auto nodeList = DependencyManager::get(); const QUuid& myNodeID = nodeList->getSessionUUID(); - if (overwriteLocalData && _simulatorID == myNodeID && !_simulatorID.isNull()) { - // we own the simulation, so we keep our transform+velocities and remove any related dirty flags - // rather than accept the values in the packet - _position = savePosition; - _rotation = saveRotation; - _velocity = saveVelocity; - _angularVelocity = saveAngularVelocity; - _dirtyFlags &= ~(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES); + if (overwriteLocalData) { + if (_simulatorID == myNodeID && !_simulatorID.isNull()) { + // we own the simulation, so we keep our transform+velocities and remove any related dirty flags + // rather than accept the values in the packet + _position = savePosition; + _rotation = saveRotation; + _velocity = saveVelocity; + _angularVelocity = saveAngularVelocity; + _dirtyFlags &= ~(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES); + } else { + _lastSimulated = now; + } } return bytesRead; From ac0609ea920bdfe0b54fc72af127c337700838cb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 28 May 2015 16:29:43 -0700 Subject: [PATCH 08/30] also render bbox when debugging simulator owner --- .../src/RenderableModelEntityItem.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 75b29002f3..35309b0799 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -112,8 +112,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); - bool drawAsModel = hasModel(); - glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); @@ -125,8 +123,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { highlightSimulationOwnership = (getSimulatorID() == myNodeID); } - bool didDraw = false; - if (drawAsModel && !highlightSimulationOwnership) { + if (hasModel()) { remapTextures(); glPushMatrix(); { @@ -179,19 +176,20 @@ void RenderableModelEntityItem::render(RenderArgs* args) { if (args && (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE)) { if (movingOrAnimating) { _model->renderInScene(alpha, args); - didDraw = true; } } else { _model->renderInScene(alpha, args); - didDraw = true; } } } } glPopMatrix(); - } - if (!didDraw) { + if (highlightSimulationOwnership) { + glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); + RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor); + } + } else { glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor); } From 8177512432c7442036f48d641eb72543ee6df0ed Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 29 May 2015 11:35:50 -0700 Subject: [PATCH 09/30] send all TerseUpdate properties when one changes --- libraries/entities/src/EntityItem.cpp | 15 +++++++++++++++ libraries/entities/src/EntityItem.h | 2 ++ libraries/entities/src/EntityItemProperties.cpp | 5 ++++- libraries/entities/src/EntityItemProperties.h | 3 +++ .../entities/src/EntityScriptingInterface.cpp | 12 +++++++++++- 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4a9b64d1ca..cc951ac16b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -925,6 +925,21 @@ EntityItemProperties EntityItem::getProperties() const { return properties; } +void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) const { + // a TerseUpdate includes the transform and its derivatives + properties._position = _position; + properties._velocity = _velocity; + properties._rotation = _rotation; + properties._angularVelocity = _angularVelocity; + properties._acceleration = _acceleration; + + properties._positionChanged = true; + properties._velocityChanged = true; + properties._rotationChanged = true; + properties._angularVelocityChanged = true; + properties._accelerationChanged = true; +} + bool EntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 4f2132bef4..6f9dc54e7a 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -347,6 +347,8 @@ public: quint64 getLastEditedFromRemote() { return _lastEditedFromRemote; } + void getAllTerseUpdateProperties(EntityItemProperties& properties) const; + protected: static bool _sendPhysicsUpdates; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 856c1a1cb4..583cf15b9c 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1156,4 +1156,7 @@ AABox EntityItemProperties::getAABox() const { return AABox(rotatedExtentsRelativeToRegistrationPoint); } - +bool EntityItemProperties::hasTerseUpdateChanges() const { + // a TerseUpdate includes the transform and its derivatives + return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged; +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 26c26bd474..dbe2e926c9 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -195,6 +195,8 @@ public: void setVoxelDataDirty() { _voxelDataChanged = true; } + bool hasTerseUpdateChanges() const; + private: QUuid _id; bool _idSet; @@ -215,6 +217,7 @@ private: QStringList _textureNames; glm::vec3 _naturalDimensions; }; + Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 54d2ea705e..d684fbf2fc 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -146,7 +146,17 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& EntityItemProperties modifiedProperties = properties; entity->setLastBroadcast(usecTimestampNow()); modifiedProperties.setType(entity->getType()); - bidForSimulationOwnership(modifiedProperties); + if (modifiedProperties.hasTerseUpdateChanges()) { + // we make a bid for (or assert) our simulation ownership + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + modifiedProperties.setSimulatorID(myNodeID); + + if (entity->getSimulatorID() == myNodeID) { + // we think we already own simulation, so make sure we send ALL TerseUpdate properties + entity->getAllTerseUpdateProperties(modifiedProperties); + } + } queueEntityMessage(PacketTypeEntityEdit, entityID, modifiedProperties); return id; } From e253687dcd09b02c9b19184bc82c037fc4711109 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 29 May 2015 12:06:07 -0700 Subject: [PATCH 10/30] fix fail case for DS pings --- libraries/networking/src/NodeList.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 26cd3f1425..b6d7c2026a 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -505,12 +505,19 @@ void NodeList::pingPunchForDomainServer() { } else { if (_domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) { // if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat + qCDebug(networking) << "No ping replies received from domain-server with ID" + << uuidStringWithoutCurlyBraces(_domainHandler.getICEClientID()) << "-" << "re-sending ICE query."; _domainHandler.getICEPeer().softReset(); handleICEConnectionToDomainServer(); + + return; } } + qDebug() << "Sending domain server ping to" + << _domainHandler.getICEPeer().getLocalSocket() << _domainHandler.getICEPeer().getPublicSocket(); + flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendPingsToDS); // send the ping packet to the local and public sockets for this node From f98305dd555f351db3deb35a9d4752a12e8616d5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 29 May 2015 12:45:03 -0700 Subject: [PATCH 11/30] CR comments --- libraries/octree/src/ViewFrustum.h | 4 ++-- libraries/render-utils/src/GlWindow.cpp | 18 +++--------------- libraries/render-utils/src/GlWindow.h | 11 +++++------ libraries/render-utils/src/OffscreenGlCanvas.h | 2 +- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 43bc1d992e..cd1e010818 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -52,8 +52,8 @@ public: void getFocalLength(float focalLength) { _focalLength = focalLength; } // getters for lens attributes - const glm::mat4& getProjection() const { return _projection; }; - const glm::mat4& getView() const { return _view; }; + const glm::mat4& getProjection() const { return _projection; } + const glm::mat4& getView() const { return _view; } float getWidth() const { return _width; } float getHeight() const { return _height; } float getFieldOfView() const { return _fieldOfView; } diff --git a/libraries/render-utils/src/GlWindow.cpp b/libraries/render-utils/src/GlWindow.cpp index b395140518..ec294dca5e 100644 --- a/libraries/render-utils/src/GlWindow.cpp +++ b/libraries/render-utils/src/GlWindow.cpp @@ -10,24 +10,12 @@ #include #include +#include -static QSurfaceFormat getDefaultFormat() { - QSurfaceFormat format; - // Qt Quick may need a depth and stencil buffer. Always make sure these are available. - format.setDepthBufferSize(16); - format.setStencilBufferSize(8); - format.setVersion(4, 1); -#ifdef DEBUG - format.setOption(QSurfaceFormat::DebugContext); -#endif - format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile); - return format; +GlWindow::GlWindow(QOpenGLContext* shareContext) : GlWindow(getDefaultOpenGlSurfaceFormat(), shareContext) { } -GlWindow::GlWindow(QOpenGLContext * shareContext) : GlWindow(getDefaultFormat(), shareContext) { -} - -GlWindow::GlWindow(const QSurfaceFormat& format, QOpenGLContext * shareContext) { +GlWindow::GlWindow(const QSurfaceFormat& format, QOpenGLContext* shareContext) { setSurfaceType(QSurface::OpenGLSurface); setFormat(format); _context = new QOpenGLContext; diff --git a/libraries/render-utils/src/GlWindow.h b/libraries/render-utils/src/GlWindow.h index 69fb09932e..57108c6e37 100644 --- a/libraries/render-utils/src/GlWindow.h +++ b/libraries/render-utils/src/GlWindow.h @@ -11,24 +11,23 @@ #define hifi_GlWindow_h #include -#include class QOpenGLContext; class QOpenGLDebugLogger; class GlWindow : public QWindow { public: - GlWindow(QOpenGLContext * shareContext = nullptr); - GlWindow(const QSurfaceFormat& format, QOpenGLContext * shareContext = nullptr); + GlWindow(QOpenGLContext* shareContext = nullptr); + GlWindow(const QSurfaceFormat& format, QOpenGLContext* shareContext = nullptr); virtual ~GlWindow(); void makeCurrent(); void doneCurrent(); void swapBuffers(); private: - QOpenGLContext * _context{ nullptr }; + QOpenGLContext* _context{ nullptr }; #ifdef DEBUG - QOpenGLDebugLogger * _logger{ nullptr }; + QOpenGLDebugLogger* _logger{ nullptr }; #endif }; -#endif \ No newline at end of file +#endif diff --git a/libraries/render-utils/src/OffscreenGlCanvas.h b/libraries/render-utils/src/OffscreenGlCanvas.h index c9578f5a7c..399737ddb8 100644 --- a/libraries/render-utils/src/OffscreenGlCanvas.h +++ b/libraries/render-utils/src/OffscreenGlCanvas.h @@ -31,7 +31,7 @@ protected: QOpenGLContext _context; QOffscreenSurface _offscreenSurface; #ifdef DEBUG - QOpenGLDebugLogger * _logger{ nullptr }; + QOpenGLDebugLogger* _logger{ nullptr }; #endif }; From 77b45e2b4adbba05f32e5d77092545cd6f033ad0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 29 May 2015 12:59:55 -0700 Subject: [PATCH 12/30] Adding missing files --- libraries/shared/src/GLHelpers.cpp | 16 ++++++++++++++++ libraries/shared/src/GLHelpers.h | 23 +++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 libraries/shared/src/GLHelpers.cpp create mode 100644 libraries/shared/src/GLHelpers.h diff --git a/libraries/shared/src/GLHelpers.cpp b/libraries/shared/src/GLHelpers.cpp new file mode 100644 index 0000000000..f2371e49bb --- /dev/null +++ b/libraries/shared/src/GLHelpers.cpp @@ -0,0 +1,16 @@ +#include "GLHelpers.h" + + +QSurfaceFormat getDefaultOpenGlSurfaceFormat() { + QSurfaceFormat format; + // Qt Quick may need a depth and stencil buffer. Always make sure these are available. + format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); + format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); + format.setVersion(4, 1); +#ifdef DEBUG + format.setOption(QSurfaceFormat::DebugContext); +#endif + // FIXME move to core as soon as possible + format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile); + return format; +} diff --git a/libraries/shared/src/GLHelpers.h b/libraries/shared/src/GLHelpers.h new file mode 100644 index 0000000000..dc9f0f3140 --- /dev/null +++ b/libraries/shared/src/GLHelpers.h @@ -0,0 +1,23 @@ +// +// Created by Bradley Austin Davis 2015/05/29 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_GLHelpers_h +#define hifi_GLHelpers_h + +#include + +// 16 bits of depth precision +#define DEFAULT_GL_DEPTH_BUFFER_BITS 16 +// 8 bits of stencil buffer (typically you really only need 1 bit for functionality +// but GL implementations usually just come with buffer sizes in multiples of 8) +#define DEFAULT_GL_STENCIL_BUFFER_BITS 8 + +QSurfaceFormat getDefaultOpenGlSurfaceFormat(); + +#endif From 33fde7c66d610bb05d2460352ee7fdd518be73e2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 29 May 2015 13:21:24 -0700 Subject: [PATCH 13/30] remove extra debug --- libraries/networking/src/NodeList.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b6d7c2026a..44aa5bc644 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -515,9 +515,6 @@ void NodeList::pingPunchForDomainServer() { } } - qDebug() << "Sending domain server ping to" - << _domainHandler.getICEPeer().getLocalSocket() << _domainHandler.getICEPeer().getPublicSocket(); - flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendPingsToDS); // send the ping packet to the local and public sockets for this node From 6e2378e676ab5590dcd40826780a0cd76a4bc5dd Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 29 May 2015 16:19:41 -0700 Subject: [PATCH 14/30] Sound parameter tweaks. --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index dea184087b..ce9689aa18 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1133,7 +1133,7 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT const float energy = mass * linearVelocity * linearVelocity / 2.0f; const glm::vec3 position = collision.contactPoint; const float COLLISION_ENERGY_AT_FULL_VOLUME = 0.5f; - const float COLLISION_MINIMUM_VOLUME = 0.001f; + const float COLLISION_MINIMUM_VOLUME = 0.005f; const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) { return; @@ -1149,7 +1149,7 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT } // This is a hack. Quiet sound aren't really heard at all, so we compress everything to the range [1-c, 1], if we play it all. - const float COLLISION_SOUND_COMPRESSION_RANGE = 0.7f; + const float COLLISION_SOUND_COMPRESSION_RANGE = 1.0f; // This section could be removed when the value is 1, but let's see how it goes. float volume = energyFactorOfFull; volume = (volume * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE); @@ -1180,10 +1180,11 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons return; } // Don't respond to small continuous contacts. - const float COLLISION_MINUMUM_PENETRATION = 0.005f; + const float COLLISION_MINUMUM_PENETRATION = 0.002f; if ((collision.type != CONTACT_EVENT_TYPE_START) && (glm::length(collision.penetration) < COLLISION_MINUMUM_PENETRATION)) { return; } + qCDebug(entitiesrenderer) << "*" << collision.type << glm::length(collision.penetration); // See if we should play sounds EntityTree* entityTree = static_cast(_tree); From 9c56f6270e4e704dd51ccc31abafa75ad96b2ed8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 29 May 2015 16:21:57 -0700 Subject: [PATCH 15/30] Remove debug print. --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index ce9689aa18..8f034409ae 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1184,7 +1184,6 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons if ((collision.type != CONTACT_EVENT_TYPE_START) && (glm::length(collision.penetration) < COLLISION_MINUMUM_PENETRATION)) { return; } - qCDebug(entitiesrenderer) << "*" << collision.type << glm::length(collision.penetration); // See if we should play sounds EntityTree* entityTree = static_cast(_tree); From 4c83c6918d2682caad29b55272bcef445fbd6e11 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Fri, 29 May 2015 16:44:16 -0700 Subject: [PATCH 16/30] fixed avatar billboards not displaying --- interface/src/Application.cpp | 19 +++++++++++++------ interface/src/Application.h | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 877b5ed931..d4f87056cd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3103,20 +3103,27 @@ PickRay Application::computePickRay(float x, float y) const { QImage Application::renderAvatarBillboard() { auto primaryFramebuffer = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer)); - + + // clear the alpha channel so the background is transparent + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + // the "glow" here causes an alpha of one Glower glower; - + const int BILLBOARD_SIZE = 64; renderRearViewMirror(QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE, BILLBOARD_SIZE, BILLBOARD_SIZE), true); - + QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32); glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); - + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); - + return image; } @@ -3160,7 +3167,7 @@ const ViewFrustum* Application::getDisplayViewFrustum() const { return &_displayViewFrustum; } -void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs::RenderSide renderSide) { +void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billboard, RenderArgs::RenderSide renderSide) { activeRenderingThread = QThread::currentThread(); PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("display"); diff --git a/interface/src/Application.h b/interface/src/Application.h index 2226c97b99..b6efb6420b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -270,7 +270,7 @@ public: QImage renderAvatarBillboard(); - void displaySide(Camera& whichCamera, bool selfAvatarOnly = false, RenderArgs::RenderSide renderSide = RenderArgs::MONO); + void displaySide(Camera& whichCamera, bool selfAvatarOnly = false, bool billboard = false, RenderArgs::RenderSide renderSide = RenderArgs::MONO); /// Stores the current modelview matrix as the untranslated view matrix to use for transforms and the supplied vector as /// the view matrix translation. From c19115a4852b870cf7a6f994870aabe21f4f677f Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Fri, 29 May 2015 16:53:01 -0700 Subject: [PATCH 17/30] actually fixed billboard --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d4f87056cd..0db28af77f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3383,7 +3383,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb false, selfAvatarOnly); } - { + if (!billboard) { DependencyManager::get()->setAmbientLightMode(getRenderAmbientLight()); auto skyStage = DependencyManager::get()->getSkyStage(); DependencyManager::get()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity()); @@ -3586,7 +3586,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { // render rear mirror view glPushMatrix(); - displaySide(_mirrorCamera, true); + displaySide(_mirrorCamera, true, billboard); glPopMatrix(); if (!billboard) { From 8dda86f80dcfcc8b1cab715abd8d2239ce61363e Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Fri, 29 May 2015 17:02:51 -0700 Subject: [PATCH 18/30] added brownianFun script to apply random forces to balls in a pen --- examples/example/brownianFun.js | 247 ++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 examples/example/brownianFun.js diff --git a/examples/example/brownianFun.js b/examples/example/brownianFun.js new file mode 100644 index 0000000000..b7f7fe3b32 --- /dev/null +++ b/examples/example/brownianFun.js @@ -0,0 +1,247 @@ +var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +var SOUND_PATH = HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/"; +var soundURLS = ["67LCollision01.wav", "67LCollision02.wav", "airhockey_hit1.wav"]; +var FLOOR_SIZE = 10; +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(FLOOR_SIZE * 1.5, Quat.getFront(Camera.getOrientation()))); +var WALL_WIDTH = .1; +var FLOOR_HEIGHT_OFFSET = -2; +var WALL_HEIGHT = FLOOR_SIZE / 4; +var BALL_DROP_HEIGHT = center.y + WALL_HEIGHT; +var NUM_BALLS = 50; +var BALL_RADIUS = 1; +var BROWNIAN_INTERVAL_TIME = 16; +var BROWNIAN_FORCE_RANGE = 5; +var SPAWN_TIME = 50; +var spawnCount = 0; + +var brownianMotionActivated = false; +var buttonOffColor = { + red: 250, + green: 10, + blue: 10 +}; +var buttonOnColor = { + red: 10, + green: 200, + blue: 100 +}; + +var bounds = { + xMin: center.x - FLOOR_SIZE / 2, + xMax: center.x + FLOOR_SIZE / 2, + zMin: center.z - FLOOR_SIZE / 2, + zMax: center.z + FLOOR_SIZE / 2 +}; +var balls = []; + +var screenSize = Controller.getViewportDimensions(); +var BUTTON_SIZE = 32; +var PADDING = 3; + +var brownianButton = Overlays.addOverlay("image", { + x: screenSize.x / 2 - BUTTON_SIZE, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/blocks.png", + color: buttonOffColor, + alpha: 1 +}); + +var floor = Entities.addEntity({ + type: 'Box', + position: Vec3.sum(center, { + x: 0, + y: FLOOR_HEIGHT_OFFSET, + z: 0 + }), + dimensions: { + x: FLOOR_SIZE, + y: WALL_WIDTH, + z: FLOOR_SIZE + }, + color: { + red: 100, + green: 100, + blue: 100 + } +}); + +var rightWall = Entities.addEntity({ + type: 'Box', + position: Vec3.sum(center, { + x: FLOOR_SIZE / 2, + y: FLOOR_HEIGHT_OFFSET / 2, + z: 0 + }), + dimensions: { + x: WALL_WIDTH, + y: WALL_HEIGHT, + z: FLOOR_SIZE + }, + color: { + red: 120, + green: 100, + blue: 120 + } +}); + +var leftWall = Entities.addEntity({ + type: 'Box', + position: Vec3.sum(center, { + x: -FLOOR_SIZE / 2, + y: FLOOR_HEIGHT_OFFSET / 2, + z: 0 + }), + dimensions: { + x: WALL_WIDTH, + y: WALL_HEIGHT, + z: FLOOR_SIZE + }, + color: { + red: 120, + green: 100, + blue: 120 + } +}); + +var backWall = Entities.addEntity({ + type: 'Box', + position: Vec3.sum(center, { + x: 0, + y: FLOOR_HEIGHT_OFFSET / 2, + z: -FLOOR_SIZE / 2, + }), + dimensions: { + x: FLOOR_SIZE, + y: WALL_HEIGHT, + z: WALL_WIDTH + }, + color: { + red: 120, + green: 100, + blue: 120 + } +}); + +var frontWall = Entities.addEntity({ + type: 'Box', + position: Vec3.sum(center, { + x: 0, + y: FLOOR_HEIGHT_OFFSET / 2, + z: FLOOR_SIZE / 2, + }), + dimensions: { + x: FLOOR_SIZE, + y: WALL_HEIGHT, + z: WALL_WIDTH + }, + color: { + red: 120, + green: 100, + blue: 120 + } +}); + +var spawnInterval = Script.setInterval(spawnBalls, SPAWN_TIME); + +function spawnBalls() { + balls.push(Entities.addEntity({ + type: "Sphere", + shapeType: "sphere", + position: { + x: randFloat(bounds.xMin, bounds.xMax), + y: BALL_DROP_HEIGHT, + z: randFloat(bounds.zMin, bounds.zMax) + }, + dimensions: { + x: BALL_RADIUS, + y: BALL_RADIUS, + z: BALL_RADIUS + }, + color: { + red: randFloat(100, 150), + green: randFloat(20, 80), + blue: randFloat(10, 180) + }, + ignoreCollisions: false, + collisionsWillMove: true, + gravity: { + x: 0, + y: -9.9, + z: 0 + }, + velocity: { + x: 0, + y: -.25, + z: 0 + }, + collisionSoundURL: SOUND_PATH + soundURLS[randInt(0, soundURLS.length - 1)] + })); + spawnCount++; + if (spawnCount === NUM_BALLS) { + Script.clearInterval(spawnInterval); + } +} + +function mousePressEvent(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + if (clickedOverlay == brownianButton) { + brownianMotionActivated = !brownianMotionActivated; + if (brownianMotionActivated) { + brownianInterval = Script.setInterval(bumpBalls, BROWNIAN_INTERVAL_TIME); + Overlays.editOverlay(brownianButton, { + color: buttonOnColor + }) + } else { + Script.clearInterval(brownianInterval); + Overlays.editOverlay(brownianButton, { + color: buttonOffColor + }) + } + } +} + +function bumpBalls() { + balls.forEach(function(ball) { + var props = Entities.getEntityProperties(ball); + var newVelocity = Vec3.sum(props.velocity, { + x: (Math.random() - 0.5) * BROWNIAN_FORCE_RANGE, + y: 0, + z: (Math.random() - 0.5) * BROWNIAN_FORCE_RANGE + }); + Entities.editEntity(ball, { + velocity: newVelocity + }); + }); +} + +function cleanup() { + Entities.deleteEntity(floor); + Entities.deleteEntity(rightWall); + Entities.deleteEntity(leftWall); + Entities.deleteEntity(backWall); + Entities.deleteEntity(frontWall); + balls.forEach(function(ball) { + Entities.deleteEntity(ball); + }); + Overlays.deleteOverlay(brownianButton); +} + +function randFloat(low, high) { + return Math.floor(low + Math.random() * (high - low)); +} + + +function randInt(low, high) { + return Math.floor(randFloat(low, high)); +} + +Script.scriptEnding.connect(cleanup); + + +Script.scriptEnding.connect(cleanup); +Controller.mousePressEvent.connect(mousePressEvent); \ No newline at end of file From 2a931119bdee73512aef793e8417adaa8274bc89 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 28 May 2015 12:17:21 -0700 Subject: [PATCH 19/30] Replace expand/collapse dialog animation with fade in/out --- interface/resources/qml/controls/Dialog.qml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index f32b4e2e66..15c8b7bafc 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -31,7 +31,7 @@ DialogBase { * Support for animating the dialog in and out. */ enabled: false - scale: 0.0 + opacity: 0.0 // The offscreen UI will enable an object, rather than manipulating it's // visibility, so that we can do animations in both directions. Because @@ -40,20 +40,20 @@ DialogBase { // opacity, and then when the target animation value is reached, we can // modify the visibility onEnabledChanged: { - scale = enabled ? 1.0 : 0.0 + opacity = enabled ? 1.0 : 0.0 } // The actual animator - Behavior on scale { + Behavior on opacity { NumberAnimation { - duration: root.animationDuration - easing.type: Easing.InOutBounce + duration: 300 + easing.type: Easing.OutCubic } } - // Once we're scaled to 0, disable the dialog's visibility - onScaleChanged: { - visible = (scale != 0.0); + // Once we're transparent, disable the dialog's visibility + onOpacityChanged: { + visible = (opacity != 0.0); } // Some dialogs should be destroyed when they become invisible, From a43593c3b2dde13b495ada6d965459db97041b36 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 29 May 2015 15:23:55 -0700 Subject: [PATCH 20/30] Dialog opacity transition tidying --- interface/resources/qml/controls/Dialog.qml | 4 ++-- interface/resources/qml/styles/HifiConstants.qml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index 15c8b7bafc..aa14e2fcba 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -31,7 +31,7 @@ DialogBase { * Support for animating the dialog in and out. */ enabled: false - opacity: 0.0 + opacity: 1.0 // The offscreen UI will enable an object, rather than manipulating it's // visibility, so that we can do animations in both directions. Because @@ -46,7 +46,7 @@ DialogBase { // The actual animator Behavior on opacity { NumberAnimation { - duration: 300 + duration: animationDuration easing.type: Easing.OutCubic } } diff --git a/interface/resources/qml/styles/HifiConstants.qml b/interface/resources/qml/styles/HifiConstants.qml index d24e9ca9be..702a396afb 100644 --- a/interface/resources/qml/styles/HifiConstants.qml +++ b/interface/resources/qml/styles/HifiConstants.qml @@ -56,6 +56,6 @@ Item { QtObject { id: effects - readonly property int fadeInDuration: 400 + readonly property int fadeInDuration: 300 } } From 881941b64dd89acd98af8b00b8f4db41c8e02abc Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 29 May 2015 15:25:35 -0700 Subject: [PATCH 21/30] Restyle address bar dialog Made the dialog a stand-alone QML file that doesn't inherit from other dialog QML files so that don't need to rework all dialogs right now. --- interface/resources/qml/AddressBarDialog.qml | 185 ++++++++++++------- 1 file changed, 116 insertions(+), 69 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index e12452472a..42a0a7dd7e 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -1,91 +1,124 @@ import Hifi 1.0 import QtQuick 2.3 +import QtQuick.Controls 1.2 import "controls" import "styles" -Dialog { +Item { id: root HifiConstants { id: hifi } - - title: "Go to..." + objectName: "AddressBarDialog" - contentImplicitWidth: addressBarDialog.implicitWidth - contentImplicitHeight: addressBarDialog.implicitHeight - destroyOnCloseButton: false + property int animationDuration: hifi.effects.fadeInDuration + property bool destroyOnInvisible: false + property bool destroyOnCloseButton: true - onVisibleChanged: { - if (!visible) { - reset(); - } - } + implicitWidth: addressBarDialog.implicitWidth + implicitHeight: addressBarDialog.implicitHeight + + x: parent ? parent.width / 2 - width / 2 : 0 + y: parent ? parent.height / 2 - height / 2 : 0 + + AddressBarDialog { + id: addressBarDialog + + implicitWidth: box.width + implicitHeight: addressLine.height + hifi.layout.spacing * 2 + + Border { + id: box + + width: 512 + height: parent.height + border.width: 0 + radius: 6 + color: "#ededee" + + MouseArea { + id: boxDrag + + anchors.fill: parent + + drag { + target: root + minimumX: 0 + minimumY: 0 + maximumX: root.parent ? root.parent.width - root.width : 0 + maximumY: root.parent ? root.parent.height - root.height : 0 + } + } + + TextInput { + id: addressLine + + anchors.fill: parent + anchors.leftMargin: hifi.layout.spacing * 2 + anchors.rightMargin: hifi.layout.spacing * 2 + anchors.topMargin: hifi.layout.spacing + anchors.bottomMargin: hifi.layout.spacing + + font.pointSize: 15 + helperText: "Go to: place, @user, /path, network address" + + onAccepted: { + event.accepted + addressBarDialog.loadAddress(addressLine.text) + } + } + } + } + + // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions. + // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property + // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the + // visibility. + enabled: false + opacity: 1.0 onEnabledChanged: { + opacity = enabled ? 1.0 : 0.0 if (enabled) { addressLine.forceActiveFocus(); } } - onParentChanged: { - if (enabled && visible) { - addressLine.forceActiveFocus(); + + Behavior on opacity { + // Animate opacity. + NumberAnimation { + duration: animationDuration + easing.type: Easing.OutCubic } } + onOpacityChanged: { + // Once we're transparent, disable the dialog's visibility. + visible = (opacity != 0.0) + } + + onVisibleChanged: { + if (!visible) { + reset() + + // Some dialogs should be destroyed when they become invisible. + if (destroyOnInvisible) { + destroy() + } + } + + } + + function close() { + // The close function performs the same way as the OffscreenUI class: don't do anything but manipulate the enabled flag + // and let the other mechanisms decide if the window should be destroyed after the close animation completes. + if (destroyOnCloseButton) { + destroyOnInvisible = true + } + enabled = false + } + function reset() { addressLine.text = "" - goButton.source = "../images/address-bar-submit.svg" - } - - AddressBarDialog { - id: addressBarDialog - // The client area - x: root.clientX - y: root.clientY - implicitWidth: 512 - implicitHeight: border.height + hifi.layout.spacing * 4 - - - Border { - id: border - height: 64 - anchors.left: parent.left - anchors.leftMargin: hifi.layout.spacing * 2 - anchors.right: goButton.left - anchors.rightMargin: hifi.layout.spacing - anchors.verticalCenter: parent.verticalCenter - TextInput { - id: addressLine - anchors.fill: parent - helperText: "domain, location, @user, /x,y,z" - anchors.margins: hifi.layout.spacing - onAccepted: { - event.accepted - addressBarDialog.loadAddress(addressLine.text) - } - } - } - - Image { - id: goButton - width: 32 - height: 32 - anchors.right: parent.right - anchors.rightMargin: hifi.layout.spacing * 2 - source: "../images/address-bar-submit.svg" - anchors.verticalCenter: parent.verticalCenter - - MouseArea { - anchors.fill: parent - onClicked: { - parent.source = "../images/address-bar-submit-active.svg" - addressBarDialog.loadAddress(addressLine.text) - } - } - } - } - - Keys.onEscapePressed: { - enabled = false; } function toggleOrGo() { @@ -95,8 +128,22 @@ Dialog { addressBarDialog.loadAddress(addressLine.text) } } - + + Keys.onEscapePressed: { + enabled = false + } + + Keys.onPressed: { + switch(event.key) { + case Qt.Key_W: + if (event.modifiers == Qt.ControlModifier) { + event.accepted = true + enabled = false + } + break + } + } + Keys.onReturnPressed: toggleOrGo() Keys.onEnterPressed: toggleOrGo() } - From 05e251eba4f5e2d77524df1bf64e0cd32cc41df3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 30 May 2015 12:01:50 -0700 Subject: [PATCH 22/30] Add icon to address bar dialog --- .../resources/images/address-bar-icon.svg | 63 +++++++++++++++++++ .../images/address-bar-submit-active.svg | 18 ------ .../resources/images/address-bar-submit.svg | 18 ------ interface/resources/qml/AddressBarDialog.qml | 48 +++++++++++--- 4 files changed, 102 insertions(+), 45 deletions(-) create mode 100644 interface/resources/images/address-bar-icon.svg delete mode 100644 interface/resources/images/address-bar-submit-active.svg delete mode 100644 interface/resources/images/address-bar-submit.svg diff --git a/interface/resources/images/address-bar-icon.svg b/interface/resources/images/address-bar-icon.svg new file mode 100644 index 0000000000..a6d67aabae --- /dev/null +++ b/interface/resources/images/address-bar-icon.svg @@ -0,0 +1,63 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/interface/resources/images/address-bar-submit-active.svg b/interface/resources/images/address-bar-submit-active.svg deleted file mode 100644 index 313b366033..0000000000 --- a/interface/resources/images/address-bar-submit-active.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/interface/resources/images/address-bar-submit.svg b/interface/resources/images/address-bar-submit.svg deleted file mode 100644 index df4d7e90f6..0000000000 --- a/interface/resources/images/address-bar-submit.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 42a0a7dd7e..a3f7c6ba18 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -23,8 +23,12 @@ Item { AddressBarDialog { id: addressBarDialog - implicitWidth: box.width - implicitHeight: addressLine.height + hifi.layout.spacing * 2 + property int iconOverlap: 15 // Let the circle overlap window edges and rectangular part of dialog + property int maximumX: root.parent.width - root.width + property int maximumY: root.parent.height - root.height + + implicitWidth: box.width + icon.width - iconOverlap * 2 + implicitHeight: addressLine.height + hifi.layout.spacing * 2 Border { id: box @@ -35,6 +39,8 @@ Item { radius: 6 color: "#ededee" + x: icon.width - addressBarDialog.iconOverlap * 2 // W.r.t. addressBarDialog + MouseArea { id: boxDrag @@ -42,18 +48,18 @@ Item { drag { target: root - minimumX: 0 - minimumY: 0 - maximumX: root.parent ? root.parent.width - root.width : 0 - maximumY: root.parent ? root.parent.height - root.height : 0 - } + minimumX: 0 + minimumY: 0 + maximumX: root.parent ? addressBarDialog.maximumX : 0 + maximumY: root.parent ? addressBarDialog.maximumY : 0 + } } TextInput { id: addressLine anchors.fill: parent - anchors.leftMargin: hifi.layout.spacing * 2 + anchors.leftMargin: addressBarDialog.iconOverlap + hifi.layout.spacing * 2 anchors.rightMargin: hifi.layout.spacing * 2 anchors.topMargin: hifi.layout.spacing anchors.bottomMargin: hifi.layout.spacing @@ -67,7 +73,31 @@ Item { } } } - } + + Image { + id: icon + source: "../images/address-bar-icon.svg" + width: 80 + height: 80 + anchors.right: box.left + anchors.rightMargin: -addressBarDialog.iconOverlap + anchors.verticalCenter: parent.verticalCenter + + MouseArea { + id: iconDrag + + anchors.fill: parent + + drag { + target: root + minimumX: 0 + minimumY: 0 + maximumX: root.parent ? addressBarDialog.maximumX : 0 + maximumY: root.parent ? addressBarDialog.maximumY : 0 + } + } + } + } // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions. // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property From e41b4c1b7b4bfed4c8ba7af6a86a0842ddfc7ada Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 31 May 2015 20:56:11 -0700 Subject: [PATCH 23/30] Add new address bar dialog error message Implemented as a new simple InformationDialog so that can try it out without reworking MessageDialog immediately for all QML UI. --- .../images/address-bar-error-close.svg | 50 ++++++ .../images/address-bar-error-icon.svg | 3 + interface/resources/qml/ErrorDialog.qml | 152 ++++++++++++++++++ interface/src/Application.cpp | 14 +- interface/src/ui/AddressBarDialog.cpp | 6 +- libraries/ui/src/ErrorDialog.cpp | 38 +++++ libraries/ui/src/ErrorDialog.h | 46 ++++++ libraries/ui/src/OffscreenUi.cpp | 10 ++ libraries/ui/src/OffscreenUi.h | 2 + 9 files changed, 311 insertions(+), 10 deletions(-) create mode 100644 interface/resources/images/address-bar-error-close.svg create mode 100644 interface/resources/images/address-bar-error-icon.svg create mode 100644 interface/resources/qml/ErrorDialog.qml create mode 100644 libraries/ui/src/ErrorDialog.cpp create mode 100644 libraries/ui/src/ErrorDialog.h diff --git a/interface/resources/images/address-bar-error-close.svg b/interface/resources/images/address-bar-error-close.svg new file mode 100644 index 0000000000..45a4dd4635 --- /dev/null +++ b/interface/resources/images/address-bar-error-close.svg @@ -0,0 +1,50 @@ + +image/svg+xml \ No newline at end of file diff --git a/interface/resources/images/address-bar-error-icon.svg b/interface/resources/images/address-bar-error-icon.svg new file mode 100644 index 0000000000..f2453fb0d4 --- /dev/null +++ b/interface/resources/images/address-bar-error-icon.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/interface/resources/qml/ErrorDialog.qml b/interface/resources/qml/ErrorDialog.qml new file mode 100644 index 0000000000..72b2c24e90 --- /dev/null +++ b/interface/resources/qml/ErrorDialog.qml @@ -0,0 +1,152 @@ +// +// ErrorDialog.qml +// +// Created by David Rowe on 30 May 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 +import "controls" +import "styles" + +Item { + id: root + HifiConstants { id: hifi } + + property int animationDuration: hifi.effects.fadeInDuration + property bool destroyOnInvisible: true + + Component.onCompleted: { + enabled = true + } + + onParentChanged: { + if (visible && enabled) { + forceActiveFocus(); + } + } + + implicitWidth: content.implicitWidth + implicitHeight: content.implicitHeight + + x: parent ? parent.width / 2 - width / 2 : 0 + y: parent ? parent.height / 2 - height / 2 : 0 + + Hifi.ErrorDialog { + id: content + + implicitWidth: box.width + implicitHeight: icon.height + hifi.layout.spacing * 2 + + Border { + id: box + + width: 512 + color: "#ebebeb" + radius: 2 + border.width: 1 + border.color: "#000000" + + Image { + id: icon + + source: "../images/address-bar-error-icon.svg" + width: 40 + height: 40 + anchors { + left: parent.left + leftMargin: hifi.layout.spacing + verticalCenter: parent.verticalCenter + } + } + + Text { + id: messageText + + font.pointSize: 10 + font.weight: Font.Bold + + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + + text: content.text + } + + Image { + source: "../images/address-bar-error-close.svg" + width: 20 + height: 20 + anchors { + right: parent.right + rightMargin: hifi.layout.spacing * 2 + verticalCenter: parent.verticalCenter + } + MouseArea { + anchors.fill: parent + onClicked: { + content.accept(); + } + } + } + } + } + + // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions. + // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property + // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the + // visibility. + enabled: false + opacity: 1.0 + + onEnabledChanged: { + opacity = enabled ? 1.0 : 0.0 + } + + Behavior on opacity { + // Animate opacity. + NumberAnimation { + duration: animationDuration + easing.type: Easing.OutCubic + } + } + + onOpacityChanged: { + // Once we're transparent, disable the dialog's visibility. + visible = (opacity != 0.0) + } + + onVisibleChanged: { + if (!visible) { + // Some dialogs should be destroyed when they become invisible. + if (destroyOnInvisible) { + destroy() + } + } + } + + Keys.onPressed: { + if (event.modifiers === Qt.ControlModifier) + switch (event.key) { + case Qt.Key_W: + event.accepted = true + content.accept() + break + } else switch (event.key) { + case Qt.Key_Escape: + case Qt.Key_Back: + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + content.accept() + break + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 877b5ed931..d43306715a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -55,40 +55,41 @@ #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 #include #include #include +#include #include #include #include #include #include #include -#include -#include -#include +#include #include "Application.h" #include "AudioClient.h" @@ -799,6 +800,7 @@ void Application::initializeGL() { void Application::initializeUi() { AddressBarDialog::registerType(); + ErrorDialog::registerType(); LoginDialog::registerType(); MessageDialog::registerType(); VrMenu::registerType(); diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 837702d253..3c3a843586 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -36,12 +36,10 @@ void AddressBarDialog::loadAddress(const QString& address) { } void AddressBarDialog::displayAddressOfflineMessage() { - OffscreenUi::information("Address offline", - "That user or place is currently offline."); + OffscreenUi::error("That user or place is currently offline"); } void AddressBarDialog::displayAddressNotFoundMessage() { - OffscreenUi::information("Address not found", - "There is no address information for that user or place."); + OffscreenUi::error("There is no address information for that user or place"); } diff --git a/libraries/ui/src/ErrorDialog.cpp b/libraries/ui/src/ErrorDialog.cpp new file mode 100644 index 0000000000..ab36ef8d36 --- /dev/null +++ b/libraries/ui/src/ErrorDialog.cpp @@ -0,0 +1,38 @@ +// +// ErrorDialog.cpp +// +// Created by David Rowe on 30 May 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ErrorDialog.h" + +HIFI_QML_DEF(ErrorDialog) + +ErrorDialog::ErrorDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { +} + +ErrorDialog::~ErrorDialog() { +} + +QString ErrorDialog::text() const { + return _text; +} + +void ErrorDialog::setVisible(bool v) { + OffscreenQmlDialog::setVisible(v); +} + +void ErrorDialog::setText(const QString& arg) { + if (arg != _text) { + _text = arg; + emit textChanged(); + } +} + +void ErrorDialog::accept() { + OffscreenQmlDialog::accept(); +} diff --git a/libraries/ui/src/ErrorDialog.h b/libraries/ui/src/ErrorDialog.h new file mode 100644 index 0000000000..665090da1a --- /dev/null +++ b/libraries/ui/src/ErrorDialog.h @@ -0,0 +1,46 @@ +// +// ErrorDialog.h +// +// Created by David Rowe on 30 May 2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once + +#ifndef hifi_ErrorDialog_h +#define hifi_ErrorDialog_h + +#include "OffscreenQmlDialog.h" + +class ErrorDialog : public OffscreenQmlDialog +{ + Q_OBJECT + HIFI_QML_DECL + +private: + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + +public: + ErrorDialog(QQuickItem* parent = 0); + virtual ~ErrorDialog(); + + QString text() const; + +public slots: + virtual void setVisible(bool v); + void setText(const QString& arg); + +signals: + void textChanged(); + +protected slots: + virtual void accept(); + +private: + QString _text; +}; + +#endif // hifi_ErrorDialog_h diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 449657ca04..d94cad20d2 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -13,6 +13,7 @@ #include #include #include +#include "ErrorDialog.h" #include "MessageDialog.h" @@ -130,6 +131,15 @@ void OffscreenUi::critical(const QString& title, const QString& text, static_cast(MessageDialog::Critical), buttons); } +void OffscreenUi::error(const QString& text) { + ErrorDialog* pDialog{ nullptr }; + ErrorDialog::show([&](QQmlContext* ctx, QObject* item) { + pDialog = item->findChild(); + pDialog->setText(text); + }); + pDialog->setEnabled(true); +} + OffscreenUi::ButtonCallback OffscreenUi::NO_OP_CALLBACK = [](QMessageBox::StandardButton) {}; diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index d3567bbb5e..4d0044e775 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -115,6 +115,8 @@ public: static void critical(const QString& title, const QString& text, ButtonCallback callback = NO_OP_CALLBACK, QMessageBox::StandardButtons buttons = QMessageBox::Ok); + + static void error(const QString& text); // Interim dialog in new style }; #endif From 9a8bb67f37cae3b9e328fa49227daebe6329b246 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 31 May 2015 20:56:26 -0700 Subject: [PATCH 24/30] Tidy address bar dialog code --- interface/resources/qml/AddressBarDialog.qml | 56 ++++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index a3f7c6ba18..2a18c21bdf 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -1,3 +1,13 @@ +// +// AddressBarDialog.qml +// +// Created by Austin Davis on 2015/04/14 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + import Hifi 1.0 import QtQuick 2.3 import QtQuick.Controls 1.2 @@ -12,7 +22,6 @@ Item { property int animationDuration: hifi.effects.fadeInDuration property bool destroyOnInvisible: false - property bool destroyOnCloseButton: true implicitWidth: addressBarDialog.implicitWidth implicitHeight: addressBarDialog.implicitHeight @@ -24,8 +33,8 @@ Item { id: addressBarDialog property int iconOverlap: 15 // Let the circle overlap window edges and rectangular part of dialog - property int maximumX: root.parent.width - root.width - property int maximumY: root.parent.height - root.height + property int maximumX: root.parent ? root.parent.width - root.width : 0 + property int maximumY: root.parent ? root.parent.height - root.height : 0 implicitWidth: box.width + icon.width - iconOverlap * 2 implicitHeight: addressLine.height + hifi.layout.spacing * 2 @@ -39,13 +48,10 @@ Item { radius: 6 color: "#ededee" - x: icon.width - addressBarDialog.iconOverlap * 2 // W.r.t. addressBarDialog + x: icon.width - addressBarDialog.iconOverlap * 2 // Relative to addressBarDialog MouseArea { - id: boxDrag - anchors.fill: parent - drag { target: root minimumX: 0 @@ -58,18 +64,20 @@ Item { TextInput { id: addressLine - anchors.fill: parent - anchors.leftMargin: addressBarDialog.iconOverlap + hifi.layout.spacing * 2 - anchors.rightMargin: hifi.layout.spacing * 2 - anchors.topMargin: hifi.layout.spacing - anchors.bottomMargin: hifi.layout.spacing + anchors { + fill: parent + leftMargin: addressBarDialog.iconOverlap + hifi.layout.spacing * 2 + rightMargin: hifi.layout.spacing * 2 + topMargin: hifi.layout.spacing + bottomMargin: hifi.layout.spacing + } font.pointSize: 15 helperText: "Go to: place, @user, /path, network address" onAccepted: { - event.accepted - addressBarDialog.loadAddress(addressLine.text) + event.accepted = true // Generates erroneous error in program log, "ReferenceError: event is not defined". + addressBarDialog.loadAddress(addressLine.text) } } } @@ -79,15 +87,14 @@ Item { source: "../images/address-bar-icon.svg" width: 80 height: 80 - anchors.right: box.left - anchors.rightMargin: -addressBarDialog.iconOverlap - anchors.verticalCenter: parent.verticalCenter + anchors { + right: box.left + rightMargin: -addressBarDialog.iconOverlap + verticalCenter: parent.verticalCenter + } MouseArea { - id: iconDrag - anchors.fill: parent - drag { target: root minimumX: 0 @@ -138,15 +145,6 @@ Item { } - function close() { - // The close function performs the same way as the OffscreenUI class: don't do anything but manipulate the enabled flag - // and let the other mechanisms decide if the window should be destroyed after the close animation completes. - if (destroyOnCloseButton) { - destroyOnInvisible = true - } - enabled = false - } - function reset() { addressLine.text = "" } From 45331e5a0e0edc2c1c747c93c3ba6d0b63f819d4 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Mon, 1 Jun 2015 09:37:54 -0700 Subject: [PATCH 25/30] removed sounds from grab script --- examples/grab.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/examples/grab.js b/examples/grab.js index f1e1b6571c..7ed69e9664 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -36,8 +36,6 @@ var angularVelocity = { z: 0 }; -var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav"); -var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav"); var DROP_DISTANCE = 5.0; var DROP_COLOR = { @@ -90,10 +88,6 @@ function mousePressEvent(event) { gravity: {x: 0, y: 0, z: 0} }); - Audio.playSound(grabSound, { - position: props.position, - volume: 0.4 - }); } } @@ -135,11 +129,6 @@ function mouseReleaseEvent() { }); targetPosition = null; - Audio.playSound(grabSound, { - position: entityProps.position, - volume: 0.25 - }); - } } From b166e24ff5daac0bef3c4d401e7c5d7b6f307f65 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 1 Jun 2015 11:06:33 -0700 Subject: [PATCH 26/30] Fix initial fade-in --- interface/resources/qml/AddressBarDialog.qml | 2 +- interface/resources/qml/ErrorDialog.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 2a18c21bdf..30890c0050 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -111,7 +111,7 @@ Item { // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the // visibility. enabled: false - opacity: 1.0 + opacity: 0.0 onEnabledChanged: { opacity = enabled ? 1.0 : 0.0 diff --git a/interface/resources/qml/ErrorDialog.qml b/interface/resources/qml/ErrorDialog.qml index 72b2c24e90..c0f8132f14 100644 --- a/interface/resources/qml/ErrorDialog.qml +++ b/interface/resources/qml/ErrorDialog.qml @@ -104,7 +104,7 @@ Item { // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the // visibility. enabled: false - opacity: 1.0 + opacity: 0.0 onEnabledChanged: { opacity = enabled ? 1.0 : 0.0 From 293af425acaf17f3b61ff79317d4d5643fa48436 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 1 Jun 2015 12:49:26 -0700 Subject: [PATCH 27/30] remove vhacd block from windows build guide --- BUILD_WIN.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 169077ed78..e905a83ace 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -75,21 +75,6 @@ To prevent these problems, install OpenSSL yourself. Download the following bina 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. -###vhacd -Download it directly from https://github.com/virneo/v-hacd - -To build it run the following commands - 1. cd src\ - 2. mkdir build - 3. cd build - 4. cmake .. - -Build using visual studio 2013. Build ALL_BUILD and INSTALL targets both in Release and Debug. - -This will create an output folder with include and lib directory inside it. - -Either copy the contents of output folder to ENV %HIFI_LIB_DIR%/vhacd or create an environment variable VHACD_ROOT_DIR to this output directory. - ###Build High Fidelity using Visual Studio Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake. From 9c52c79fe1df20bcf00ed5ff2c00fa95c3cd8860 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 1 Jun 2015 13:29:36 -0700 Subject: [PATCH 28/30] Combine icon and input areas into a single SVG --- .../{address-bar-icon.svg => address-bar.svg} | 23 +++-- interface/resources/qml/AddressBarDialog.qml | 89 +++++++++---------- 2 files changed, 57 insertions(+), 55 deletions(-) rename interface/resources/images/{address-bar-icon.svg => address-bar.svg} (81%) diff --git a/interface/resources/images/address-bar-icon.svg b/interface/resources/images/address-bar.svg similarity index 81% rename from interface/resources/images/address-bar-icon.svg rename to interface/resources/images/address-bar.svg index a6d67aabae..0a472cbe4e 100644 --- a/interface/resources/images/address-bar-icon.svg +++ b/interface/resources/images/address-bar.svg @@ -8,14 +8,14 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" - width="200" + width="1440" height="200" data-icon="map-marker" data-container-transform="translate(24)" - viewBox="0 0 200 200" + viewBox="0 0 1440 200" id="svg4136" inkscape:version="0.91 r13725" - sodipodi:docname="address-icon.svg"> + sodipodi:docname="address-bar.svg"> @@ -24,7 +24,7 @@ image/svg+xml - + @@ -43,13 +43,22 @@ inkscape:window-height="1057" id="namedview4140" showgrid="false" - inkscape:zoom="0.43359375" - inkscape:cx="64" - inkscape:cy="144.72072" + inkscape:zoom="0.8671875" + inkscape:cx="707.02439" + inkscape:cy="52.468468" inkscape:window-x="72" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:current-layer="svg4136" /> + Date: Mon, 1 Jun 2015 13:54:14 -0700 Subject: [PATCH 29/30] Untabify --- interface/resources/qml/AddressBarDialog.qml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8d11fa9bb5..3893c26f3c 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -100,9 +100,9 @@ Item { } // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions. - // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property - // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the - // visibility. + // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property + // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the + // visibility. enabled: false opacity: 0.0 @@ -122,7 +122,7 @@ Item { } onOpacityChanged: { - // Once we're transparent, disable the dialog's visibility. + // Once we're transparent, disable the dialog's visibility. visible = (opacity != 0.0) } @@ -130,10 +130,10 @@ Item { if (!visible) { reset() - // Some dialogs should be destroyed when they become invisible. - if (destroyOnInvisible) { - destroy() - } + // Some dialogs should be destroyed when they become invisible. + if (destroyOnInvisible) { + destroy() + } } } From 037246ee3cb60b0f0b35f7abf3645907df85b710 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 1 Jun 2015 15:58:43 -0700 Subject: [PATCH 30/30] implement meta item for model entities --- .../src/RenderableModelEntityItem.cpp | 59 ++++++++++++++----- .../src/RenderableModelEntityItem.h | 2 + 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 97a756e181..c4930f1225 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "EntityTreeRenderer.h" #include "EntitiesRendererLogging.h" @@ -136,21 +137,63 @@ bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { return ready; } +class RenderableModelEntityItemMeta { +public: + RenderableModelEntityItemMeta(EntityItemPointer entity) : entity(entity){ } + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + EntityItemPointer entity; +}; + +namespace render { + template <> const ItemKey payloadGetKey(const RenderableModelEntityItemMeta::Pointer& payload) { + return ItemKey::Builder::opaqueShape(); + } + + template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) { + if (payload && payload->entity) { + return payload->entity->getAABox(); + } + return render::Item::Bound(); + } + template <> void payloadRender(const RenderableModelEntityItemMeta::Pointer& payload, RenderArgs* args) { + if (args) { + args->_elementsTouched++; + if (payload && payload->entity) { + payload->entity->render(args); + } + } + } +} + bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { + _myMetaItem = scene->allocateID(); + + auto renderData = RenderableModelEntityItemMeta::Pointer(new RenderableModelEntityItemMeta(self)); + auto renderPayload = render::PayloadPointer(new RenderableModelEntityItemMeta::Payload(renderData)); + + pendingChanges.resetItem(_myMetaItem, renderPayload); + if (_model) { return _model->addToScene(scene, pendingChanges); } - return false; + + return true; } void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { + pendingChanges.removeItem(_myMetaItem); if (_model) { _model->removeFromScene(scene, pendingChanges); } } + +// NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items, and it handles +// the per frame simulation/update that might be required if the models properties changed. void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); @@ -209,20 +252,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } _needsInitialSimulation = false; } - - if (_model->isActive()) { - // TODO: this is the majority of model render time. And rendering of a cube model vs the basic Box render - // is significantly more expensive. Is there a way to call this that doesn't cost us as much? - PerformanceTimer perfTimer("model->render"); - // filter out if not needed to render - if (args && (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE)) { - if (movingOrAnimating) { - _model->renderInScene(alpha, args); - } - } else { - _model->renderInScene(alpha, args); - } - } } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 55044e5d4d..837890a960 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -77,6 +77,8 @@ private: QStringList _originalTextures; bool _originalTexturesRead; QVector> _points; + + render::ItemID _myMetaItem; }; #endif // hifi_RenderableModelEntityItem_h