diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index a74b5bee90..3cf01bdc9f 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -237,8 +237,9 @@ void MetavoxelSession::update() { // go back to the beginning with the current packet and note that there's a delta pending _sequencer.getOutputStream().getUnderlying().device()->seek(start); - MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID }; - out << QVariant::fromValue(msg); + MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID, sendRecord->getPacketNumber(), + _sequencer.getIncomingPacketNumber() }; + out << (_reliableDeltaMessage = QVariant::fromValue(msg)); _sequencer.endPacket(); } else { @@ -254,8 +255,9 @@ void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { } PacketRecord* MetavoxelSession::maybeCreateSendRecord() const { - return _reliableDeltaChannel ? new PacketRecord(_reliableDeltaLOD, _reliableDeltaData) : - new PacketRecord(_lod, _sender->getData()); + return _reliableDeltaChannel ? new PacketRecord(_sequencer.getOutgoingPacketNumber(), + _reliableDeltaLOD, _reliableDeltaData) : new PacketRecord(_sequencer.getOutgoingPacketNumber(), + _lod, _sender->getData()); } void MetavoxelSession::handleMessage(const QVariant& message) { @@ -290,8 +292,7 @@ void MetavoxelSession::sendPacketGroup(int alreadySent) { for (int i = 0; i < additionalPackets; i++) { Bitstream& out = _sequencer.startPacket(); if (_reliableDeltaChannel) { - MetavoxelDeltaPendingMessage msg = { _reliableDeltaID }; - out << QVariant::fromValue(msg); + out << _reliableDeltaMessage; } else { out << QVariant(); } diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 70c49fad64..840041e0f0 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -134,6 +134,7 @@ private: MetavoxelLOD _reliableDeltaLOD; Bitstream::WriteMappings _reliableDeltaWriteMappings; int _reliableDeltaID; + QVariant _reliableDeltaMessage; }; /// Handles persistence in a separate thread. diff --git a/cmake/modules/FindSDL2.cmake b/cmake/modules/FindSDL2.cmake index f24af555a2..6ef9ffa52a 100644 --- a/cmake/modules/FindSDL2.cmake +++ b/cmake/modules/FindSDL2.cmake @@ -102,9 +102,12 @@ #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("sdl2") FIND_PATH(SDL2_INCLUDE_DIR SDL.h HINTS + ${SDL2_SEARCH_DIRS} $ENV{SDL2} PATH_SUFFIXES include/SDL2 include SDL2 i686-w64-mingw32/include/SDL2 @@ -124,6 +127,7 @@ FIND_PATH(SDL2_INCLUDE_DIR SDL.h IF(CMAKE_SIZEOF_VOID_P EQUAL 8) FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2 HINTS + ${SDL2_SEARCH_DIRS} $ENV{SDL2} PATH_SUFFIXES lib64 lib lib/x64 @@ -138,6 +142,7 @@ IF(CMAKE_SIZEOF_VOID_P EQUAL 8) ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8) FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2 HINTS + ${SDL2_SEARCH_DIRS} $ENV{SDL2} PATH_SUFFIXES lib lib/x86 @@ -161,6 +166,7 @@ IF(NOT SDL2_BUILDING_LIBRARY) FIND_LIBRARY(SDL2MAIN_LIBRARY NAMES SDL2main HINTS + ${SDL2_SEARCH_DIRS} $ENV{SDL2} PATH_SUFFIXES lib64 lib lib/x64 @@ -176,6 +182,7 @@ IF(NOT SDL2_BUILDING_LIBRARY) FIND_LIBRARY(SDL2MAIN_LIBRARY NAMES SDL2main HINTS + ${SDL2_SEARCH_DIRS} $ENV{SDL2} PATH_SUFFIXES lib lib/x86 diff --git a/examples/headMove.js b/examples/headMove.js index 086ef80abc..236130e23d 100644 --- a/examples/headMove.js +++ b/examples/headMove.js @@ -5,174 +5,181 @@ // Created by Philip Rosedale on September 8, 2014 // Copyright 2014 High Fidelity, Inc. // -// Press the spacebar and move/turn your head to move around. +// Press the spacebar and then use your head to move and turn. Pull back to see your body. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // var debug = false; +var willMove = false; + +var warpActive = false; +var warpPosition = { x: 0, y: 0, z: 0 }; + +// Overlays to show target location + +var WARP_SPHERE_SIZE = 0.15; +var warpSphere = Overlays.addOverlay("sphere", { + position: { x: 0, y: 0, z: 0 }, + size: WARP_SPHERE_SIZE, + color: { red: 200, green: 0, blue: 0 }, + alpha: 0.5, + solid: true, + visible: false, +}); + +var WARP_LINE_HEIGHT = 5; +var warpLine = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z:0 }, + end: { x: 0, y: 0, z: 0 }, + color: { red: 0, green: 255, blue: 255}, + alpha: 1, + lineWidth: 5, + visible: false, +}); var movingWithHead = false; var headStartPosition, headStartDeltaPitch, headStartFinalPitch, headStartRoll, headStartYaw; +var deltaYaw = 0.0; +var keyDownTime = 0.0; +var watchAvatar = false; +var oldMode; -var HEAD_MOVE_DEAD_ZONE = 0.05; -var HEAD_STRAFE_DEAD_ZONE = 0.03; -var HEAD_ROTATE_DEAD_ZONE = 10.0; -var HEAD_YAW_RATE = 1.5; -var HEAD_PITCH_RATE = 1.0; -var WALL_BOUNCE = 10000.0; -var FIXED_WALK_VELOCITY = 1.5; - -// Modify these values to tweak the strength of the motion. -// A larger *FACTOR increases the speed. -// A lower SHORT_TIMESCALE makes the motor achieve full speed faster. -var HEAD_VELOCITY_FWD_FACTOR = 10.0; -var HEAD_VELOCITY_LEFT_FACTOR = 0.0; -var HEAD_VELOCITY_UP_FACTOR = 20.0; -var SHORT_TIMESCALE = 0.01; -var VERY_LARGE_TIMESCALE = 1000000.0; - -var xAxis = {x:1.0, y:0.0, z:0.0 }; -var yAxis = {x:0.0, y:1.0, z:0.0 }; -var zAxis = {x:0.0, y:0.0, z:1.0 }; - -// If these values are set to something -var maxVelocity = 1.25; -var noFly = true; -var fixedWalkVelocity = true; - -//var roomLimits = { xMin: 618, xMax: 635.5, zMin: 528, zMax: 552.5 }; -var roomLimits = { xMin: -1.0, xMax: -1.0, zMin: -1.0, zMax: -1.0 }; - -function isInRoom(position) { - var BUFFER = 2.0; - if (roomLimits.xMin < 0) { - return false; - } - if ((position.x > (roomLimits.xMin - BUFFER)) && - (position.x < (roomLimits.xMax + BUFFER)) && - (position.z > (roomLimits.zMin - BUFFER)) && - (position.z < (roomLimits.zMax + BUFFER))) - { - return true; - } else { - return false; - } +function saveCameraState() { + oldMode = Camera.getMode(); + Camera.setMode("independent"); } -function moveWithHead(deltaTime) { - var position = MyAvatar.position; - var motorTimescale = VERY_LARGE_TIMESCALE; +function restoreCameraState() { + Camera.setMode(oldMode); +} + +function activateWarp() { + if (warpActive) return; + warpActive = true; + + updateWarp(); +} + +var TRIGGER_PULLBACK_DISTANCE = 0.04; +var WATCH_AVATAR_DISTANCE = 1.5; +var MAX_WARP_YAW = 40.0; +var MAX_PULLBACK_YAW = 5.0; + +var sound = new Sound("http://public.highfidelity.io/sounds/Footsteps/FootstepW2Right-12db.wav"); +function playSound() { + var options = new AudioInjectionOptions(); + var position = MyAvatar.position; + options.position = position; + options.volume = 0.5; + Audio.playSound(sound, options); +} + +var WARP_SMOOTHING = 0.90; +var WARP_START_TIME = 0.50; +var WARP_START_DISTANCE = 1.0; +var WARP_SENSITIVITY = 0.15; + +function updateWarp() { + if (!warpActive) return; + + var look = Quat.getFront(Camera.getOrientation()); + var deltaPosition = Vec3.subtract(MyAvatar.getTrackedHeadPosition(), headStartPosition); + var deltaPitch = MyAvatar.getHeadFinalPitch() - headStartFinalPitch; + deltaYaw = MyAvatar.getHeadFinalYaw() - headStartYaw; + + willMove = (!watchAvatar && (Math.abs(deltaYaw) < MAX_WARP_YAW) && (keyDownTime > WARP_START_TIME)); + + if (willMove) { + //var distance = Math.pow((deltaPitch - WARP_PITCH_DEAD_ZONE) * WARP_SENSITIVITY, 2.0); + var distance = Math.exp(deltaPitch * WARP_SENSITIVITY) * WARP_START_DISTANCE; + var warpDirection = Vec3.normalize({ x: look.x, y: 0, z: look.z }); + warpPosition = Vec3.mix(Vec3.sum(MyAvatar.position, Vec3.multiply(warpDirection, distance)), warpPosition, WARP_SMOOTHING); + } + + var height = MyAvatar.getEyePosition().y - MyAvatar.position.y; + + if (!watchAvatar && (Math.abs(deltaYaw) < MAX_PULLBACK_YAW) && (deltaPosition.z > TRIGGER_PULLBACK_DISTANCE)) { + saveCameraState(); + var cameraPosition = Vec3.subtract(MyAvatar.position, Vec3.multiplyQbyV(Camera.getOrientation(), { x: 0, y: -height, z: -height * WATCH_AVATAR_DISTANCE })); + Camera.setPosition(cameraPosition); + watchAvatar = true; + } + + // Adjust overlays to match warp position + Overlays.editOverlay(warpSphere, { + position: warpPosition, + visible: willMove, + }); + Overlays.editOverlay(warpLine, { + position: Vec3.sum(warpPosition, { x: 0, y: -WARP_LINE_HEIGHT / 2.0, z: 0 }), + end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT / 2.0, z: 0 }), + visible: willMove, + }); +} + +function finishWarp() { + if (!warpActive) return; + warpActive = false; + Overlays.editOverlay(warpSphere, { + visible: false, + }); + Overlays.editOverlay(warpLine, { + visible: false, + }); + if (willMove) { + MyAvatar.position = warpPosition; + playSound(); + } +} + +function update(deltaTime) { + if (movingWithHead) { - var deltaYaw = MyAvatar.getHeadFinalYaw() - headStartYaw; - var deltaPitch = MyAvatar.getHeadDeltaPitch() - headStartDeltaPitch; - var deltaRoll = MyAvatar.getHeadFinalRoll() - headStartRoll; - var velocity = MyAvatar.getVelocity(); - var position = MyAvatar.position; - var neckPosition = MyAvatar.getNeckPosition(); - var bodyLocalCurrentHeadVector = Vec3.subtract(neckPosition, position); - bodyLocalCurrentHeadVector = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), bodyLocalCurrentHeadVector); - var headDelta = Vec3.subtract(bodyLocalCurrentHeadVector, headStartPosition); - headDelta.y = 0.0; // Don't respond to any of the vertical component of head motion - headDelta = Vec3.multiplyQbyV(MyAvatar.orientation, headDelta); - headDelta = Vec3.multiplyQbyV(Quat.inverse(Camera.getOrientation()), headDelta); - - var length = Vec3.length(headDelta); - - if (length > 1.0) { - // Needs fixed! Right now sometimes reported neck position jumps to a bad value - headDelta.x = headDelta.y = headDelta.z = 0.0; - length = 0.0; - return; - } - - // Thrust based on leaning forward and side-to-side - var targetVelocity = {x:0.0, y:0.0, z:0.0}; - if (length > HEAD_MOVE_DEAD_ZONE) { - //headDelta = Vec3.normalize(headDelta); - //targetVelocity = Vec3.multiply(headDelta, FIXED_WALK_VELOCITY); - targetVelocity = Vec3.multiply(headDelta, HEAD_VELOCITY_FWD_FACTOR); - } - /* - if (Math.abs(headDelta.z) > HEAD_MOVE_DEAD_ZONE) { - if (fixedWalkVelocity) { - targetVelocity = Vec3.multiply(zAxis, headDelta.z > 0 ? FIXED_WALK_VELOCITY : -FIXED_WALK_VELOCITY); - } else { - targetVelocity = Vec3.multiply(zAxis, headDelta.z * HEAD_VELOCITY_FWD_FACTOR); - } - } - if (Math.abs(headDelta.x) > HEAD_STRAFE_DEAD_ZONE) { - var deltaVelocity = Vec3.multiply(xAxis, headDelta.x * HEAD_VELOCITY_LEFT_FACTOR); - targetVelocity = Vec3.sum(targetVelocity, deltaVelocity); - } - */ - if (Math.abs(deltaYaw) > HEAD_ROTATE_DEAD_ZONE) { - var orientation = Quat.multiply(Quat.angleAxis((deltaYaw + deltaRoll) * HEAD_YAW_RATE * deltaTime, yAxis), MyAvatar.orientation); - MyAvatar.orientation = orientation; - } - - // Thrust Up/Down based on head pitch - if (!noFly) { - var deltaVelocity = Vec3.multiply(yAxis, headDelta.y * HEAD_VELOCITY_UP_FACTOR); - targetVelocity = Vec3.sum(targetVelocity, deltaVelocity); - } - // For head trackers, adjust pitch by head pitch - MyAvatar.headPitch += deltaPitch * HEAD_PITCH_RATE * deltaTime; - - // apply the motor - MyAvatar.motorVelocity = targetVelocity; - motorTimescale = SHORT_TIMESCALE; + keyDownTime += deltaTime; + updateWarp(); } - - // Check against movement box limits - if (isInRoom(position)) { - var thrust = { x: 0, y: 0, z: 0 }; - // use thrust to constrain the avatar to the space - if (position.x < roomLimits.xMin) { - thrust.x += (roomLimits.xMin - position.x) * WALL_BOUNCE * deltaTime; - } else if (position.x > roomLimits.xMax) { - thrust.x += (roomLimits.xMax - position.x) * WALL_BOUNCE * deltaTime; - } - if (position.z < roomLimits.zMin) { - thrust.z += (roomLimits.zMin - position.z) * WALL_BOUNCE * deltaTime; - } else if (position.z > roomLimits.zMax) { - thrust.z += (roomLimits.zMax - position.z) * WALL_BOUNCE * deltaTime; - } - MyAvatar.addThrust(thrust); - if (movingWithHead && Vec3.length(thrust) > 0.0) { - // reduce the timescale of the motor so that it won't defeat the thrust code - Vec3.print("adebug room containment thrust = ", thrust); - motorTimescale = 1000000.0; - } - } - MyAvatar.motorTimescale = motorTimescale; } Controller.keyPressEvent.connect(function(event) { if (event.text == "SPACE" && !movingWithHead) { + keyDownTime = 0.0; movingWithHead = true; - headStartPosition = Vec3.subtract(MyAvatar.getNeckPosition(), MyAvatar.position); - headStartPosition = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), headStartPosition); + headStartPosition = MyAvatar.getTrackedHeadPosition(); headStartDeltaPitch = MyAvatar.getHeadDeltaPitch(); headStartFinalPitch = MyAvatar.getHeadFinalPitch(); headStartRoll = MyAvatar.getHeadFinalRoll(); headStartYaw = MyAvatar.getHeadFinalYaw(); - // start with disabled motor -- it will be updated shortly - MyAvatar.motorTimescale = VERY_LARGE_TIMESCALE; - MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}; - MyAvatar.motorReferenceFrame = "camera"; // alternatives are: "avatar" and "world" + deltaYaw = 0.0; + warpPosition = MyAvatar.position; + activateWarp(); } }); +var TIME_FOR_TURN_AROUND = 0.50; +var TIME_FOR_TURN = 0.25; +var TURN_AROUND = 180.0; + Controller.keyReleaseEvent.connect(function(event) { if (event.text == "SPACE") { movingWithHead = false; - // disable motor by giving it an obnoxiously large timescale - MyAvatar.motorTimescale = VERY_LARGE_TIMESCALE; - MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}; + if (keyDownTime < TIME_FOR_TURN_AROUND) { + if (keyDownTime < TIME_FOR_TURN) { + var currentYaw = MyAvatar.getHeadFinalYaw(); + MyAvatar.orientation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, currentYaw, 0), MyAvatar.orientation); + } else { + MyAvatar.orientation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, TURN_AROUND, 0), MyAvatar.orientation); + } + playSound(); + } + finishWarp(); + if (watchAvatar) { + restoreCameraState(); + watchAvatar = false; + } } }); -Script.update.connect(moveWithHead); +Script.update.connect(update); diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index 8e267e9544..63121d88a9 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -97,11 +97,12 @@ EntityCameraTool = function() { that.focus = function(entityProperties) { var dim = entityProperties.dimensions; + dim = SelectionManager.worldDimensions; var size = Math.max(dim.x, Math.max(dim.y, dim.z)); that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM); - that.setFocalPoint(entityProperties.position); + that.setFocalPoint(SelectionManager.worldPosition);//entityProperties.position); that.updateCamera(); } @@ -184,7 +185,7 @@ EntityCameraTool = function() { // Scale based on current zoom level dZoom *= that.targetZoomDistance * ZOOM_SCALING; - that.targetZoomDistance = Math.max(Math.min(that.targetZoomDistance + dZoom, MAX_ZOOM_DISTANCE), MIN_ZOOM_DISTANCE); + that.targetZoomDistance = Math.max(that.targetZoomDistance + dZoom, MIN_ZOOM_DISTANCE); that.updateCamera(); } diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 75ea95a74a..b70c2187c2 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -13,6 +13,124 @@ Script.include("libraries/globals.js"); +SPACE_LOCAL = "local"; +SPACE_WORLD = "world"; + +SelectionManager = (function() { + var that = {}; + + + that.savedProperties = {}; + + that.eventListener = null; + that.selections = []; + + that.localRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); + that.localPosition = { x: 0, y: 0, z: 0 }; + that.localDimensions = { x: 0, y: 0, z: 0 }; + + that.worldRotation = Quat.fromPitchYawRollDegrees(0, 0, 0); + that.worldPosition = { x: 0, y: 0, z: 0 }; + that.worldDimensions = { x: 0, y: 0, z: 0 }; + that.centerPosition = { x: 0, y: 0, z: 0 }; + + that.saveProperties = function() { + that.savedProperties = {}; + for (var i = 0; i < that.selections.length; i++) { + var entityID = that.selections[i]; + that.savedProperties[entityID.id] = Entities.getEntityProperties(entityID); + } + }; + + that.setEventListener = function(func) { + that.eventListener = func; + }; + + that.addEntity = function(entityID) { + print("Adding: " + entityID.id); + var idx = that.selections.indexOf(entityID); + if (idx == -1) { + that.selections.push(entityID); + } + + that._update(); + }; + + that.removeEntity = function(entityID) { + var idx = that.selections.indexOf(entityID); + if (idx >= 0) { + that.selections.splice(idx, 1); + } + + that._update(); + }; + + that.clearSelections = function() { + that.selections = []; + + that._update(); + }; + + that._update = function() { + if (that.selections.length == 0) { + that.localDimensions = null; + that.localPosition = null; + that.worldDimensions = null; + that.worldPosition = null; + } else if (that.selections.length == 1) { + var properties = Entities.getEntityProperties(that.selections[0]); + that.localDimensions = properties.dimensions; + that.localPosition = properties.position; + that.localRotation = properties.rotation; + + that.worldDimensions = properties.boundingBox.dimensions; + that.worldPosition = properties.boundingBox.center; + } else { + // For 1+ selections we can only modify selections in world space + SelectionDisplay.setSpaceMode(SPACE_WORLD); + + that.localRotation = null; + that.localDimensions = null; + that.localPosition = null; + + var properties = Entities.getEntityProperties(that.selections[0]); + + var brn = properties.boundingBox.brn; + var tfl = properties.boundingBox.tfl; + + for (var i = 1; i < that.selections.length; i++) { + properties = Entities.getEntityProperties(that.selections[i]); + var bb = properties.boundingBox; + brn.x = Math.min(bb.brn.x, brn.x); + brn.y = Math.min(bb.brn.y, brn.y); + brn.z = Math.min(bb.brn.z, brn.z); + tfl.x = Math.max(bb.tfl.x, tfl.x); + tfl.y = Math.max(bb.tfl.y, tfl.y); + tfl.z = Math.max(bb.tfl.z, tfl.z); + } + + that.localDimensions = null; + that.localPosition = null; + that.worldDimensions = { + x: tfl.x - brn.x, + y: tfl.y - brn.y, + z: tfl.z - brn.z + }; + that.worldPosition = { + x: brn.x + (that.worldDimensions.x / 2), + y: brn.y + (that.worldDimensions.y / 2), + z: brn.z + (that.worldDimensions.z / 2), + }; + } + + if (that.eventListener) { + that.eventListener(); + } + }; + + return that; +})(); + SelectionDisplay = (function () { var that = {}; @@ -20,10 +138,11 @@ SelectionDisplay = (function () { var GRABBER_DISTANCE_TO_SIZE_RATIO = 0.015; + var spaceMode = SPACE_LOCAL; var mode = "UNKNOWN"; var overlayNames = new Array(); - var lastAvatarPosition = MyAvatar.position; - var lastAvatarOrientation = MyAvatar.orientation; + var lastCameraPosition = Camera.getPosition(); + var lastCameraOrientation = Camera.getOrientation(); var lastPlaneIntersection; var currentSelection = { id: -1, isKnownID: false }; @@ -112,7 +231,6 @@ SelectionDisplay = (function () { lineWidth: grabberLineWidth, }; - var highlightBox = Overlays.addOverlay("cube", { position: { x:0, y: 0, z: 0}, size: 1, @@ -146,6 +264,15 @@ SelectionDisplay = (function () { scale: 0.1, isFacingAvatar: true }); + + // var normalLine = Overlays.addOverlay("line3d", { + // visible: true, + // lineWidth: 2.0, + // start: { x: 0, y: 0, z: 0 }, + // end: { x: 0, y: 0, z: 0 }, + // color: { red: 255, green: 255, blue: 0 }, + // ignoreRayIntersection: true, + // }); var grabberLBN = Overlays.addOverlay("cube", grabberPropertiesCorner); var grabberRBN = Overlays.addOverlay("cube", grabberPropertiesCorner); @@ -176,7 +303,7 @@ SelectionDisplay = (function () { var grabberEdgeFR = Overlays.addOverlay("cube", grabberPropertiesEdge); var grabberEdgeFL = Overlays.addOverlay("cube", grabberPropertiesEdge); - var cornerEdgeFaceGrabbers = [ + var stretchHandles = [ grabberLBN, grabberRBN, grabberLBF, @@ -205,6 +332,7 @@ SelectionDisplay = (function () { grabberEdgeFL, ]; + var baseOverlayAngles = { x: 0, y: 0, z: 0 }; var baseOverlayRotation = Quat.fromVec3Degrees(baseOverlayAngles); var baseOfEntityProjectionOverlay = Overlays.addOverlay("rectangle3d", { @@ -225,6 +353,34 @@ SelectionDisplay = (function () { var rollOverlayAngles = { x: 0, y: 180, z: 0 }; var rollOverlayRotation = Quat.fromVec3Degrees(rollOverlayAngles); + var xRailOverlay = Overlays.addOverlay("line3d", { + visible: false, + lineWidth: 1.0, + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: { red: 255, green: 0, blue: 0 }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); + var yRailOverlay = Overlays.addOverlay("line3d", { + visible: false, + lineWidth: 1.0, + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: { red: 0, green: 255, blue: 0 }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); + var zRailOverlay = Overlays.addOverlay("line3d", { + visible: false, + lineWidth: 1.0, + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: { red: 0, green: 0, blue: 255 }, + ignoreRayIntersection: true, // always ignore this + visible: false, + }); + var rotateZeroOverlay = Overlays.addOverlay("line3d", { visible: false, lineWidth: 2.0, @@ -340,6 +496,24 @@ SelectionDisplay = (function () { isFacingAvatar: false }); + var allOverlays = [ + highlightBox, + selectionBox, + grabberMoveUp, + yawHandle, + pitchHandle, + rollHandle, + rotateOverlayTarget, + rotateOverlayInner, + rotateOverlayOuter, + rotateOverlayCurrent, + rotateZeroOverlay, + rotateCurrentOverlay, + xRailOverlay, + yRailOverlay, + zRailOverlay, + ].concat(stretchHandles); + overlayNames[highlightBox] = "highlightBox"; overlayNames[selectionBox] = "selectionBox"; overlayNames[baseOfEntityProjectionOverlay] = "baseOfEntityProjectionOverlay"; @@ -384,60 +558,33 @@ SelectionDisplay = (function () { overlayNames[rotateZeroOverlay] = "rotateZeroOverlay"; overlayNames[rotateCurrentOverlay] = "rotateCurrentOverlay"; + + var activeTool = null; + var grabberTools = {}; + function addGrabberTool(overlay, tool) { + grabberTools[overlay] = { + mode: tool.mode, + onBegin: tool.onBegin, + onMove: tool.onMove, + onEnd: tool.onEnd, + } + } + that.cleanup = function () { - Overlays.deleteOverlay(highlightBox); - Overlays.deleteOverlay(selectionBox); - Overlays.deleteOverlay(grabberMoveUp); - Overlays.deleteOverlay(baseOfEntityProjectionOverlay); - Overlays.deleteOverlay(grabberLBN); - Overlays.deleteOverlay(grabberLBF); - Overlays.deleteOverlay(grabberRBN); - Overlays.deleteOverlay(grabberRBF); - Overlays.deleteOverlay(grabberLTN); - Overlays.deleteOverlay(grabberLTF); - Overlays.deleteOverlay(grabberRTN); - Overlays.deleteOverlay(grabberRTF); - - Overlays.deleteOverlay(grabberTOP); - Overlays.deleteOverlay(grabberBOTTOM); - Overlays.deleteOverlay(grabberLEFT); - Overlays.deleteOverlay(grabberRIGHT); - Overlays.deleteOverlay(grabberNEAR); - Overlays.deleteOverlay(grabberFAR); - - Overlays.deleteOverlay(grabberEdgeTR); - Overlays.deleteOverlay(grabberEdgeTL); - Overlays.deleteOverlay(grabberEdgeTF); - Overlays.deleteOverlay(grabberEdgeTN); - Overlays.deleteOverlay(grabberEdgeBR); - Overlays.deleteOverlay(grabberEdgeBL); - Overlays.deleteOverlay(grabberEdgeBF); - Overlays.deleteOverlay(grabberEdgeBN); - Overlays.deleteOverlay(grabberEdgeNR); - Overlays.deleteOverlay(grabberEdgeNL); - Overlays.deleteOverlay(grabberEdgeFR); - Overlays.deleteOverlay(grabberEdgeFL); - - Overlays.deleteOverlay(yawHandle); - Overlays.deleteOverlay(pitchHandle); - Overlays.deleteOverlay(rollHandle); - - Overlays.deleteOverlay(rotateOverlayTarget); - Overlays.deleteOverlay(rotateOverlayInner); - Overlays.deleteOverlay(rotateOverlayOuter); - Overlays.deleteOverlay(rotateOverlayCurrent); - - Overlays.deleteOverlay(rotateZeroOverlay); - Overlays.deleteOverlay(rotateCurrentOverlay); - - + for (var i = 0; i < allOverlays.length; i++) { + Overlays.deleteOverlay(allOverlays[i]); + } }; that.highlightSelectable = function(entityID) { var properties = Entities.getEntityProperties(entityID); - Overlays.editOverlay(highlightBox, { visible: true, position: properties.boundingBox.center, - dimensions: properties.boundingBox.dimensions }); + Overlays.editOverlay(highlightBox, { + visible: true, + position: properties.boundingBox.center, + dimensions: properties.dimensions, + rotation: properties.rotation + }); }; that.unhighlightSelectable = function(entityID) { @@ -446,21 +593,21 @@ SelectionDisplay = (function () { that.select = function(entityID, event) { var properties = Entities.getEntityProperties(entityID); - if (currentSelection.isKnownID == true) { - that.unselect(currentSelection); - } + // if (currentSelection.isKnownID == true) { + // that.unselect(currentSelection); + // } currentSelection = entityID; entitySelected = true; - lastAvatarPosition = MyAvatar.position; - lastAvatarOrientation = MyAvatar.orientation; + lastCameraPosition = Camera.getPosition(); + lastCameraOrientation = Camera.getOrientation(); if (event !== false) { selectedEntityProperties = properties; selectedEntityPropertiesOriginalPosition = properties.position; selectedEntityPropertiesOriginalDimensions = properties.dimensions; pickRay = Camera.computePickRay(event.x, event.y); - lastPlaneIntersection = rayPlaneIntersection(pickRay, properties.position, Quat.getFront(lastAvatarOrientation)); + lastPlaneIntersection = rayPlaneIntersection(pickRay, properties.position, Quat.getFront(lastCameraOrientation)); var wantDebug = false; if (wantDebug) { @@ -493,26 +640,29 @@ SelectionDisplay = (function () { var top, far, left, bottom, near, right, boundsCenter, objectCenter, BLN, BRN, BLF, TLN, TRN, TLF, TRF; - objectCenter = { x: properties.position.x, y: properties.position.y, z: properties.position.z }; - - top = properties.boundingBox.tfl.y; - far = properties.boundingBox.tfl.z; - left = properties.boundingBox.tfl.x; + // objectCenter = { x: properties.position.x, y: properties.position.y, z: properties.position.z }; - bottom = properties.boundingBox.brn.y; - right = properties.boundingBox.brn.x; - near = properties.boundingBox.brn.z; + var dimensions; + if (spaceMode == SPACE_LOCAL) { + objectCenter = SelectionManager.localPosition; + dimensions = SelectionManager.localDimensions; + } else { + objectCenter = SelectionManager.worldPosition; + dimensions = SelectionManager.worldDimensions; + } + objectCenter = SelectionManager.worldPosition; + dimensions = SelectionManager.worldDimensions; - boundsCenter = { x: properties.boundingBox.center.x, y: properties.boundingBox.center.y, z: properties.boundingBox.center.z }; + top = objectCenter.y + (dimensions.y / 2); + far = objectCenter.z + (dimensions.z / 2); + left = objectCenter.x + (dimensions.x / 2); - BLN = { x: left, y: bottom, z: near }; - BRN = { x: right, y: bottom, z: near }; - BLF = { x: left, y: bottom, z: far }; - BRF = { x: right, y: bottom, z: far }; - TLN = { x: left, y: top, z: near }; - TRN = { x: right, y: top, z: near }; - TLF = { x: left, y: top, z: far }; - TRF = { x: right, y: top, z: far }; + bottom = objectCenter.y - (dimensions.y / 2); + near = objectCenter.z - (dimensions.z / 2); + right = objectCenter.x - (dimensions.x / 2); + + // boundsCenter = { x: properties.boundingBox.center.x, y: properties.boundingBox.center.y, z: properties.boundingBox.center.z }; + boundsCenter = objectCenter; var yawCorner; var pitchCorner; @@ -531,9 +681,10 @@ SelectionDisplay = (function () { ------------------------------*/ - if (MyAvatar.position.x > objectCenter.x) { + var cameraPosition = Camera.getPosition(); + if (cameraPosition.x > objectCenter.x) { // must be BRF or BRN - if (MyAvatar.position.z < objectCenter.z) { + if (cameraPosition.z < objectCenter.z) { yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 90, z: 0 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); @@ -598,7 +749,7 @@ SelectionDisplay = (function () { } else { // must be BLF or BLN - if (MyAvatar.position.z < objectCenter.z) { + if (cameraPosition.z < objectCenter.z) { yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 180, z: 0 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 90 }); @@ -663,7 +814,7 @@ SelectionDisplay = (function () { var translateHandlesVisible = true; var stretchHandlesVisible = true; var selectionBoxVisible = true; - if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL" || mode == "TRANSLATE_XZ") { + if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL" || mode == "TRANSLATE_X in case they Z") { rotateHandlesVisible = false; translateHandlesVisible = false; stretchHandlesVisible = false; @@ -679,41 +830,20 @@ SelectionDisplay = (function () { Overlays.editOverlay(highlightBox, { visible: false }); - Overlays.editOverlay(selectionBox, { visible: selectionBoxVisible, position: properties.boundingBox.center, - dimensions: properties.boundingBox.dimensions }); - - + var dimensions, rotation, position; + if (spaceMode == SPACE_LOCAL) { + rotation = SelectionManager.localRotation; + dimensions = SelectionManager.localDimensions; + position = SelectionManager.localPosition; + } else { + rotation = SelectionManager.worldRotation; + dimensions = SelectionManager.worldDimensions; + position = SelectionManager.worldPosition; + } + Overlays.editOverlay(grabberMoveUp, { visible: translateHandlesVisible, position: { x: boundsCenter.x, y: top + grabberMoveUpOffset, z: boundsCenter.z } }); - Overlays.editOverlay(grabberLBN, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: near } }); - Overlays.editOverlay(grabberRBN, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: near } }); - Overlays.editOverlay(grabberLBF, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: far } }); - Overlays.editOverlay(grabberRBF, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: far } }); - Overlays.editOverlay(grabberLTN, { visible: stretchHandlesVisible, position: { x: left, y: top, z: near } }); - Overlays.editOverlay(grabberRTN, { visible: stretchHandlesVisible, position: { x: right, y: top, z: near } }); - Overlays.editOverlay(grabberLTF, { visible: stretchHandlesVisible, position: { x: left, y: top, z: far } }); - Overlays.editOverlay(grabberRTF, { visible: stretchHandlesVisible, position: { x: right, y: top, z: far } }); - - - Overlays.editOverlay(grabberTOP, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: top, z: boundsCenter.z } }); - Overlays.editOverlay(grabberBOTTOM, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: bottom, z: boundsCenter.z } }); - Overlays.editOverlay(grabberLEFT, { visible: stretchHandlesVisible, position: { x: left, y: boundsCenter.y, z: boundsCenter.z } }); - Overlays.editOverlay(grabberRIGHT, { visible: stretchHandlesVisible, position: { x: right, y: boundsCenter.y, z: boundsCenter.z } }); - Overlays.editOverlay(grabberNEAR, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: boundsCenter.y, z: near } }); - Overlays.editOverlay(grabberFAR, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: boundsCenter.y, z: far } }); - - Overlays.editOverlay(grabberEdgeTR, { visible: stretchHandlesVisible, position: { x: right, y: top, z: boundsCenter.z } }); - Overlays.editOverlay(grabberEdgeTL, { visible: stretchHandlesVisible, position: { x: left, y: top, z: boundsCenter.z } }); - Overlays.editOverlay(grabberEdgeTF, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: top, z: far } }); - Overlays.editOverlay(grabberEdgeTN, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: top, z: near } }); - Overlays.editOverlay(grabberEdgeBR, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: boundsCenter.z } }); - Overlays.editOverlay(grabberEdgeBL, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: boundsCenter.z } }); - Overlays.editOverlay(grabberEdgeBF, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: bottom, z: far } }); - Overlays.editOverlay(grabberEdgeBN, { visible: stretchHandlesVisible, position: { x: boundsCenter.x, y: bottom, z: near } }); - Overlays.editOverlay(grabberEdgeNR, { visible: stretchHandlesVisible, position: { x: right, y: boundsCenter.y, z: near } }); - Overlays.editOverlay(grabberEdgeNL, { visible: stretchHandlesVisible, position: { x: left, y: boundsCenter.y, z: near } }); - Overlays.editOverlay(grabberEdgeFR, { visible: stretchHandlesVisible, position: { x: right, y: boundsCenter.y, z: far } }); - Overlays.editOverlay(grabberEdgeFL, { visible: stretchHandlesVisible, position: { x: left, y: boundsCenter.y, z: far } }); + that.updateHandles(entityID); Overlays.editOverlay(baseOfEntityProjectionOverlay, @@ -770,6 +900,22 @@ SelectionDisplay = (function () { Entities.editEntity(entityID, { localRenderAlpha: 0.1 }); }; + that.setSpaceMode = function(newSpaceMode) { + if (spaceMode != newSpaceMode) { + spaceMode = newSpaceMode; + that.updateHandles(); + } + }; + + that.toggleSpaceMode = function() { + if (spaceMode == SPACE_WORLD && SelectionManager.selections.length > 1) { + print("Local space editing is not available with multiple selections"); + return; + } + spaceMode = spaceMode == SPACE_LOCAL ? SPACE_WORLD : SPACE_LOCAL; + that.updateHandles(); + }; + that.unselectAll = function () { if (currentSelection.isKnownID == true) { that.unselect(currentSelection); @@ -778,50 +924,170 @@ SelectionDisplay = (function () { entitySelected = false; }; + that.updateHandles = function(entityID) { + if (!entitySelected) { + that.setOverlaysVisible(false); + return; + } + + var properties = Entities.getEntityProperties(entityID); + + var rotation, dimensions, position; + + if (spaceMode == SPACE_LOCAL) { + rotation = properties.rotation; + dimensions = SelectionManager.localDimensions; + position = SelectionManager.localPosition; + } else { + rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); + dimensions = SelectionManager.worldDimensions; + position = SelectionManager.worldPosition; + } + + var halfDimensions = Vec3.multiply(0.5, dimensions); + + left = -halfDimensions.x; + right = halfDimensions.x; + top = halfDimensions.y; + bottom = -halfDimensions.y; + var front = far = halfDimensions.z; + near = -halfDimensions.z; + + var LBN = { x: left, y: bottom, z: near }; + var RBN = { x: right, y: bottom, z: near }; + var LBF = { x: left, y: bottom, z: far }; + var RBF = { x: right, y: bottom, z: far }; + var LTN = { x: left, y: top, z: near }; + var RTN = { x: right, y: top, z: near }; + var LTF = { x: left, y: top, z: far }; + var RTF = { x: right, y: top, z: far }; + + var TOP = { x: 0, y: top, z: 0 }; + var BOTTOM = { x: 0, y: bottom, z: 0 }; + var LEFT = { x: left, y: 0, z: 0 }; + var RIGHT = { x: right, y: 0, z: 0 }; + var NEAR = { x: 0, y: 0, z: near }; + var FAR = { x: 0, y: 0, z: far }; + + var EdgeTR = { x: right, y: top, z: 0 }; + var EdgeTL = { x: left, y: top, z: 0 }; + var EdgeTF = { x: 0, y: top, z: front }; + var EdgeTN = { x: 0, y: top, z: near }; + var EdgeBR = { x: right, y: bottom, z: 0 }; + var EdgeBL = { x: left, y: bottom, z: 0 }; + var EdgeBF = { x: 0, y: bottom, z: front }; + var EdgeBN = { x: 0, y: bottom, z: near }; + var EdgeNR = { x: right, y: 0, z: near }; + var EdgeNL = { x: left, y: 0, z: near }; + var EdgeFR = { x: right, y: 0, z: front }; + var EdgeFL = { x: left, y: 0, z: front }; + + LBN = Vec3.multiplyQbyV(rotation, LBN); + RBN = Vec3.multiplyQbyV(rotation, RBN); + LBF = Vec3.multiplyQbyV(rotation, LBF); + RBF = Vec3.multiplyQbyV(rotation, RBF); + LTN = Vec3.multiplyQbyV(rotation, LTN); + RTN = Vec3.multiplyQbyV(rotation, RTN); + LTF = Vec3.multiplyQbyV(rotation, LTF); + RTF = Vec3.multiplyQbyV(rotation, RTF); + + TOP = Vec3.multiplyQbyV(rotation, TOP); + BOTTOM = Vec3.multiplyQbyV(rotation, BOTTOM); + LEFT = Vec3.multiplyQbyV(rotation, LEFT); + RIGHT = Vec3.multiplyQbyV(rotation, RIGHT); + NEAR = Vec3.multiplyQbyV(rotation, NEAR); + FAR = Vec3.multiplyQbyV(rotation, FAR); + + EdgeTR = Vec3.multiplyQbyV(rotation, EdgeTR); + EdgeTL = Vec3.multiplyQbyV(rotation, EdgeTL); + EdgeTF = Vec3.multiplyQbyV(rotation, EdgeTF); + EdgeTN = Vec3.multiplyQbyV(rotation, EdgeTN); + EdgeBR = Vec3.multiplyQbyV(rotation, EdgeBR); + EdgeBL = Vec3.multiplyQbyV(rotation, EdgeBL); + EdgeBF = Vec3.multiplyQbyV(rotation, EdgeBF); + EdgeBN = Vec3.multiplyQbyV(rotation, EdgeBN); + EdgeNR = Vec3.multiplyQbyV(rotation, EdgeNR); + EdgeNL = Vec3.multiplyQbyV(rotation, EdgeNL); + EdgeFR = Vec3.multiplyQbyV(rotation, EdgeFR); + EdgeFL = Vec3.multiplyQbyV(rotation, EdgeFL); + + LBN = Vec3.sum(position, LBN); + RBN = Vec3.sum(position, RBN); + LBF = Vec3.sum(position, LBF); + RBF = Vec3.sum(position, RBF); + LTN = Vec3.sum(position, LTN); + RTN = Vec3.sum(position, RTN); + LTF = Vec3.sum(position, LTF); + RTF = Vec3.sum(position, RTF); + + TOP = Vec3.sum(position, TOP); + BOTTOM = Vec3.sum(position, BOTTOM); + LEFT = Vec3.sum(position, LEFT); + RIGHT = Vec3.sum(position, RIGHT); + NEAR = Vec3.sum(position, NEAR); + FAR = Vec3.sum(position, FAR); + + EdgeTR = Vec3.sum(position, EdgeTR); + EdgeTL = Vec3.sum(position, EdgeTL); + EdgeTF = Vec3.sum(position, EdgeTF); + EdgeTN = Vec3.sum(position, EdgeTN); + EdgeBR = Vec3.sum(position, EdgeBR); + EdgeBL = Vec3.sum(position, EdgeBL); + EdgeBF = Vec3.sum(position, EdgeBF); + EdgeBN = Vec3.sum(position, EdgeBN); + EdgeNR = Vec3.sum(position, EdgeNR); + EdgeNL = Vec3.sum(position, EdgeNL); + EdgeFR = Vec3.sum(position, EdgeFR); + EdgeFL = Vec3.sum(position, EdgeFL); + + var stretchHandlesVisible = spaceMode == SPACE_LOCAL; + Overlays.editOverlay(grabberLBN, { visible: stretchHandlesVisible, rotation: rotation, position: LBN }); + Overlays.editOverlay(grabberRBN, { visible: stretchHandlesVisible, rotation: rotation, position: RBN }); + Overlays.editOverlay(grabberLBF, { visible: stretchHandlesVisible, rotation: rotation, position: LBF }); + Overlays.editOverlay(grabberRBF, { visible: stretchHandlesVisible, rotation: rotation, position: RBF }); + Overlays.editOverlay(grabberLTN, { visible: stretchHandlesVisible, rotation: rotation, position: LTN }); + Overlays.editOverlay(grabberRTN, { visible: stretchHandlesVisible, rotation: rotation, position: RTN }); + Overlays.editOverlay(grabberLTF, { visible: stretchHandlesVisible, rotation: rotation, position: LTF }); + Overlays.editOverlay(grabberRTF, { visible: stretchHandlesVisible, rotation: rotation, position: RTF }); + + Overlays.editOverlay(grabberTOP, { visible: stretchHandlesVisible, rotation: rotation, position: TOP }); + Overlays.editOverlay(grabberBOTTOM, { visible: stretchHandlesVisible, rotation: rotation, position: BOTTOM }); + Overlays.editOverlay(grabberLEFT, { visible: stretchHandlesVisible, rotation: rotation, position: LEFT }); + Overlays.editOverlay(grabberRIGHT, { visible: stretchHandlesVisible, rotation: rotation, position: RIGHT }); + Overlays.editOverlay(grabberNEAR, { visible: stretchHandlesVisible, rotation: rotation, position: NEAR }); + Overlays.editOverlay(grabberFAR, { visible: stretchHandlesVisible, rotation: rotation, position: FAR }); + + Overlays.editOverlay(selectionBox, { + position: position, + dimensions: dimensions, + rotation: rotation, + visible: true, + }); + + Overlays.editOverlay(grabberEdgeTR, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeTR }); + Overlays.editOverlay(grabberEdgeTL, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeTL }); + Overlays.editOverlay(grabberEdgeTF, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeTF }); + Overlays.editOverlay(grabberEdgeTN, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeTN }); + Overlays.editOverlay(grabberEdgeBR, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBR }); + Overlays.editOverlay(grabberEdgeBL, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBL }); + Overlays.editOverlay(grabberEdgeBF, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBF }); + Overlays.editOverlay(grabberEdgeBN, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeBN }); + Overlays.editOverlay(grabberEdgeNR, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeNR }); + Overlays.editOverlay(grabberEdgeNL, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeNL }); + Overlays.editOverlay(grabberEdgeFR, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeFR }); + Overlays.editOverlay(grabberEdgeFL, { visible: stretchHandlesVisible, rotation: rotation, position: EdgeFL }); + + }; + + that.setOverlaysVisible = function(isVisible) { + var length = allOverlays.length; + for (var i = 0; i < length; i++) { + Overlays.editOverlay(allOverlays[i], { visible: isVisible }); + } + }; + that.unselect = function (entityID) { - Overlays.editOverlay(selectionBox, { visible: false }); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { visible: false }); - Overlays.editOverlay(grabberMoveUp, { visible: false }); - Overlays.editOverlay(grabberLBN, { visible: false }); - Overlays.editOverlay(grabberLBF, { visible: false }); - Overlays.editOverlay(grabberRBN, { visible: false }); - Overlays.editOverlay(grabberRBF, { visible: false }); - Overlays.editOverlay(grabberLTN, { visible: false }); - Overlays.editOverlay(grabberLTF, { visible: false }); - Overlays.editOverlay(grabberRTN, { visible: false }); - Overlays.editOverlay(grabberRTF, { visible: false }); - - Overlays.editOverlay(grabberTOP, { visible: false }); - Overlays.editOverlay(grabberBOTTOM, { visible: false }); - Overlays.editOverlay(grabberLEFT, { visible: false }); - Overlays.editOverlay(grabberRIGHT, { visible: false }); - Overlays.editOverlay(grabberNEAR, { visible: false }); - Overlays.editOverlay(grabberFAR, { visible: false }); - - Overlays.editOverlay(grabberEdgeTR, { visible: false }); - Overlays.editOverlay(grabberEdgeTL, { visible: false }); - Overlays.editOverlay(grabberEdgeTF, { visible: false }); - Overlays.editOverlay(grabberEdgeTN, { visible: false }); - Overlays.editOverlay(grabberEdgeBR, { visible: false }); - Overlays.editOverlay(grabberEdgeBL, { visible: false }); - Overlays.editOverlay(grabberEdgeBF, { visible: false }); - Overlays.editOverlay(grabberEdgeBN, { visible: false }); - Overlays.editOverlay(grabberEdgeNR, { visible: false }); - Overlays.editOverlay(grabberEdgeNL, { visible: false }); - Overlays.editOverlay(grabberEdgeFR, { visible: false }); - Overlays.editOverlay(grabberEdgeFL, { visible: false }); - - Overlays.editOverlay(yawHandle, { visible: false }); - Overlays.editOverlay(pitchHandle, { visible: false }); - Overlays.editOverlay(rollHandle, { visible: false }); - - Overlays.editOverlay(rotateOverlayTarget, { visible: false }); - Overlays.editOverlay(rotateOverlayInner, { visible: false }); - Overlays.editOverlay(rotateOverlayOuter, { visible: false }); - Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); - - Overlays.editOverlay(rotateZeroOverlay, { visible: false }); - Overlays.editOverlay(rotateCurrentOverlay, { visible: false }); + that.setOverlaysVisible(false); Entities.editEntity(entityID, { localRenderAlpha: 1.0 }); @@ -829,1160 +1095,711 @@ SelectionDisplay = (function () { entitySelected = false; }; - that.translateXZ = function(event) { - if (!entitySelected || mode !== "TRANSLATE_XZ") { - return; // not allowed + var lastXZPick = null; + var translateXZTool = { + mode: 'TRANSLATE_XZ', + onBegin: function(event) { + SelectionManager.saveProperties(); + var position = SelectionManager.worldPosition; + var dimensions = SelectionManager.worldDimensions; + var bottom = position.y - (dimensions.y / 2) + + var pickRay = Camera.computePickRay(event.x, event.y); + lastXZPick = rayPlaneIntersection(pickRay, position, { x: 0, y: 1, z: 0 }); + }, + onEnd: function(event, reason) { + if (reason == 'cancel') { + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID.id]; + Entities.editEntity(entityID, initialProperties); + } + } + }, + onMove: function(event) { + if (!entitySelected || mode !== "TRANSLATE_XZ") { + return; // not allowed + } + + pickRay = Camera.computePickRay(event.x, event.y); + + // translate mode left/right based on view toward entity + var newIntersection = rayPlaneIntersection(pickRay, + selectedEntityPropertiesOriginalPosition, + Quat.getFront(lastCameraOrientation)); + + var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); + + var pickRay = Camera.computePickRay(event.x, event.y); + var pick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, { x: 0, y: 1, z: 0 }); + vector = Vec3.subtract(pick, lastXZPick); + lastXZPick = pick; + + var wantDebug = false; + + for (var i = 0; i < SelectionManager.selections.length; i++) { + var properties = Entities.getEntityProperties(SelectionManager.selections[i]); + var original = properties.position; + properties.position = Vec3.sum(properties.position, vector); + Entities.editEntity(SelectionManager.selections[i], properties); + + if (wantDebug) { + print("translateXZ... "); + Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); + Vec3.print(" newIntersection:", newIntersection); + Vec3.print(" vector:", vector); + Vec3.print(" originalPosition:", original); + Vec3.print(" newPosition:", properties.position); + Vec3.print(" newPosition:", newPosition); + } + } + + tooltip.updateText(selectedEntityProperties); + that.select(currentSelection, false); // TODO: this should be more than highlighted + SelectionManager._update(); } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - // this allows us to use the old editModels "shifted" logic which makes the - // up/down behavior of the mouse move "in"/"out" of the screen. - var i = Vec3.dot(vector, Quat.getRight(orientation)); - var j = Vec3.dot(vector, Quat.getUp(orientation)); - vector = Vec3.sum(Vec3.multiply(Quat.getRight(orientation), i), - Vec3.multiply(Quat.getFront(orientation), j)); - - newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, vector); - - var wantDebug = false; - if (wantDebug) { - print("translateXZ... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - Vec3.print(" originalPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" recentPosition:", selectedEntityProperties.position); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted }; - that.translateUpDown = function(event) { - if (!entitySelected || mode !== "TRANSLATE_UP_DOWN") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - // we only care about the Y axis - vector.x = 0; - vector.z = 0; - - newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, vector); - - var wantDebug = false; - if (wantDebug) { - print("translateUpDown... "); - print(" event.y:" + event.y); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - Vec3.print(" originalPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" recentPosition:", selectedEntityProperties.position); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchNEAR = function(event) { - if (!entitySelected || mode !== "STRETCH_NEAR") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldNEAR = selectedEntityPropertiesOriginalPosition.z - halfDimensions.z; - var newNEAR = oldNEAR + vector.z; - - // if near is changing, then... - // dimensions changes by: (oldNEAR - newNEAR) - var changeInDimensions = { x: 0, y: 0, z: (oldNEAR - newNEAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: 0, y: 0, z: (oldNEAR - newNEAR) * -0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchNEAR... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldNEAR:" + oldNEAR); - print(" newNEAR:" + newNEAR); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchFAR = function(event) { - if (!entitySelected || mode !== "STRETCH_FAR") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldFAR = selectedEntityPropertiesOriginalPosition.z + halfDimensions.z; - var newFAR = oldFAR + vector.z; - var changeInDimensions = { x: 0, y: 0, z: (newFAR - oldFAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: 0, y: 0, z: (newFAR - oldFAR) * 0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchFAR... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldFAR:" + oldFAR); - print(" newFAR:" + newFAR); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchTOP = function(event) { - if (!entitySelected || mode !== "STRETCH_TOP") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldTOP = selectedEntityPropertiesOriginalPosition.y + halfDimensions.y; - var newTOP = oldTOP + vector.y; - var changeInDimensions = { x: 0, y: (newTOP - oldTOP), z: 0 }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: 0, y: (newTOP - oldTOP) * 0.5, z: 0 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchTOP... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldTOP:" + oldTOP); - print(" newTOP:" + newTOP); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchBOTTOM = function(event) { - if (!entitySelected || mode !== "STRETCH_BOTTOM") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldBOTTOM = selectedEntityPropertiesOriginalPosition.y - halfDimensions.y; - var newBOTTOM = oldBOTTOM + vector.y; - var changeInDimensions = { x: 0, y: (oldBOTTOM - newBOTTOM), z: 0 }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: 0, y: (oldBOTTOM - newBOTTOM) * -0.5, z: 0 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchBOTTOM... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldBOTTOM:" + oldBOTTOM); - print(" newBOTTOM:" + newBOTTOM); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchRIGHT = function(event) { - if (!entitySelected || mode !== "STRETCH_RIGHT") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldRIGHT = selectedEntityPropertiesOriginalPosition.x + halfDimensions.x; - var newRIGHT = oldRIGHT + vector.x; - var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: 0 , z: 0 }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, y: 0, z: 0 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchRIGHT... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldRIGHT:" + oldRIGHT); - print(" newRIGHT:" + newRIGHT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchLEFT = function(event) { - if (!entitySelected || mode !== "STRETCH_LEFT") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldLEFT = selectedEntityPropertiesOriginalPosition.x - halfDimensions.x; - var newLEFT = oldLEFT + vector.x; - var changeInDimensions = { x: (oldLEFT - newLEFT), y: 0, z: 0 }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (oldLEFT - newLEFT) * -0.5, y: 0, z: 0 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchLEFT... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldLEFT:" + oldLEFT); - print(" newLEFT:" + newLEFT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchRBN = function(event) { - if (!entitySelected || mode !== "STRETCH_RBN") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldRIGHT = selectedEntityPropertiesOriginalPosition.x + halfDimensions.x; - var newRIGHT = oldRIGHT + vector.x; - - var oldBOTTOM = selectedEntityPropertiesOriginalPosition.y - halfDimensions.y; - var newBOTTOM = oldBOTTOM - vector.y; - - var oldNEAR = selectedEntityPropertiesOriginalPosition.z - halfDimensions.z; - var newNEAR = oldNEAR - vector.z; - - - var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newBOTTOM - oldBOTTOM) , z: (newNEAR - oldNEAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, - y: (newBOTTOM - oldBOTTOM) * -0.5, - z: (newNEAR - oldNEAR) * -0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchRBN... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldRIGHT:" + oldRIGHT); - print(" newRIGHT:" + newRIGHT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchLBN = function(event) { - if (!entitySelected || mode !== "STRETCH_LBN") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldLEFT = selectedEntityPropertiesOriginalPosition.x - halfDimensions.x; - var newLEFT = oldLEFT - vector.x; - - var oldBOTTOM = selectedEntityPropertiesOriginalPosition.y - halfDimensions.y; - var newBOTTOM = oldBOTTOM - vector.y; - - var oldNEAR = selectedEntityPropertiesOriginalPosition.z - halfDimensions.z; - var newNEAR = oldNEAR - vector.z; - - - var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newBOTTOM - oldBOTTOM) , z: (newNEAR - oldNEAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5, - y: (newBOTTOM - oldBOTTOM) * -0.5, - z: (newNEAR - oldNEAR) * -0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchLBN... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldLEFT:" + oldLEFT); - print(" newLEFT:" + newLEFT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchRTN = function(event) { - if (!entitySelected || mode !== "STRETCH_RTN") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldRIGHT = selectedEntityPropertiesOriginalPosition.x + halfDimensions.x; - var newRIGHT = oldRIGHT + vector.x; - - var oldTOP = selectedEntityPropertiesOriginalPosition.y + halfDimensions.y; - var newTOP = oldTOP + vector.y; - - var oldNEAR = selectedEntityPropertiesOriginalPosition.z - halfDimensions.z; - var newNEAR = oldNEAR - vector.z; - - - var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newTOP - oldTOP) , z: (newNEAR - oldNEAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, - y: (newTOP - oldTOP) * 0.5, - z: (newNEAR - oldNEAR) * -0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchRTN... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldRIGHT:" + oldRIGHT); - print(" newRIGHT:" + newRIGHT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchLTN = function(event) { - if (!entitySelected || mode !== "STRETCH_LTN") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldLEFT = selectedEntityPropertiesOriginalPosition.x - halfDimensions.x; - var newLEFT = oldLEFT - vector.x; - - var oldTOP = selectedEntityPropertiesOriginalPosition.y + halfDimensions.y; - var newTOP = oldTOP + vector.y; - - var oldNEAR = selectedEntityPropertiesOriginalPosition.z - halfDimensions.z; - var newNEAR = oldNEAR - vector.z; - - - var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newTOP - oldTOP) , z: (newNEAR - oldNEAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5, - y: (newTOP - oldTOP) * 0.5, - z: (newNEAR - oldNEAR) * -0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchLTN... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldLEFT:" + oldLEFT); - print(" newLEFT:" + newLEFT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchRBF = function(event) { - if (!entitySelected || mode !== "STRETCH_RBF") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldRIGHT = selectedEntityPropertiesOriginalPosition.x + halfDimensions.x; - var newRIGHT = oldRIGHT + vector.x; - - var oldBOTTOM = selectedEntityPropertiesOriginalPosition.y - halfDimensions.y; - var newBOTTOM = oldBOTTOM - vector.y; - - var oldFAR = selectedEntityPropertiesOriginalPosition.z + halfDimensions.z; - var newFAR = oldFAR + vector.z; - - - var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newBOTTOM - oldBOTTOM) , z: (newFAR - oldFAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, - y: (newBOTTOM - oldBOTTOM) * -0.5, - z: (newFAR - oldFAR) * 0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchRBF... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldRIGHT:" + oldRIGHT); - print(" newRIGHT:" + newRIGHT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchLBF = function(event) { - if (!entitySelected || mode !== "STRETCH_LBF") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldLEFT = selectedEntityPropertiesOriginalPosition.x - halfDimensions.x; - var newLEFT = oldLEFT - vector.x; - - var oldBOTTOM = selectedEntityPropertiesOriginalPosition.y - halfDimensions.y; - var newBOTTOM = oldBOTTOM - vector.y; - - var oldFAR = selectedEntityPropertiesOriginalPosition.z + halfDimensions.z; - var newFAR = oldFAR + vector.z; - - - var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newBOTTOM - oldBOTTOM) , z: (newFAR - oldFAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5, - y: (newBOTTOM - oldBOTTOM) * -0.5, - z: (newFAR - oldFAR) * 0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchLBF... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldLEFT:" + oldLEFT); - print(" newLEFT:" + newLEFT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchRTF = function(event) { - if (!entitySelected || mode !== "STRETCH_RTF") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldRIGHT = selectedEntityPropertiesOriginalPosition.x + halfDimensions.x; - var newRIGHT = oldRIGHT + vector.x; - - var oldTOP = selectedEntityPropertiesOriginalPosition.y + halfDimensions.y; - var newTOP = oldTOP + vector.y; - - var oldFAR = selectedEntityPropertiesOriginalPosition.z + halfDimensions.z; - var newFAR = oldFAR + vector.z; - - - var changeInDimensions = { x: (newRIGHT - oldRIGHT), y: (newTOP - oldTOP) , z: (newFAR - oldFAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (newRIGHT - oldRIGHT) * 0.5, - y: (newTOP - oldTOP) * 0.5, - z: (newFAR - oldFAR) * 0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchRTF... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldRIGHT:" + oldRIGHT); - print(" newRIGHT:" + newRIGHT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.stretchLTF = function(event) { - if (!entitySelected || mode !== "STRETCH_LTF") { - return; // not allowed - } - pickRay = Camera.computePickRay(event.x, event.y); - - // translate mode left/right based on view toward entity - var newIntersection = rayPlaneIntersection(pickRay, - selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); - - var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); - - var halfDimensions = Vec3.multiply(selectedEntityPropertiesOriginalDimensions, 0.5); - var oldLEFT = selectedEntityPropertiesOriginalPosition.x - halfDimensions.x; - var newLEFT = oldLEFT - vector.x; - - var oldTOP = selectedEntityPropertiesOriginalPosition.y + halfDimensions.y; - var newTOP = oldTOP + vector.y; - - var oldFAR = selectedEntityPropertiesOriginalPosition.z + halfDimensions.z; - var newFAR = oldFAR + vector.z; - - - var changeInDimensions = { x: (newLEFT - oldLEFT), y: (newTOP - oldTOP) , z: (newFAR - oldFAR) }; - var newDimensions = Vec3.sum(selectedEntityPropertiesOriginalDimensions, changeInDimensions); - - if (newDimensions.x < MINIMUM_DIMENSION) { - newDimensions.x = MINIMUM_DIMENSION; - } - - if (newDimensions.y < MINIMUM_DIMENSION) { - newDimensions.y = MINIMUM_DIMENSION; - } - - if (newDimensions.z < MINIMUM_DIMENSION) { - newDimensions.z = MINIMUM_DIMENSION; - } - - var changeInPosition = { x: (newLEFT - oldLEFT) * -0.5, - y: (newTOP - oldTOP) * 0.5, - z: (newFAR - oldFAR) * 0.5 }; - var newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, changeInPosition); - var wantDebug = false; - if (wantDebug) { - print("stretchLTF... "); - Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); - Vec3.print(" newIntersection:", newIntersection); - Vec3.print(" vector:", vector); - print(" oldLEFT:" + oldLEFT); - print(" newLEFT:" + newLEFT); - Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); - Vec3.print(" changeInDimensions:", changeInDimensions); - Vec3.print(" newDimensions:", newDimensions); - - Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); - Vec3.print(" changeInPosition:", changeInPosition); - Vec3.print(" newPosition:", newPosition); - } - - selectedEntityProperties.position = newPosition; - selectedEntityProperties.dimensions = newDimensions; - Entities.editEntity(currentSelection, selectedEntityProperties); - tooltip.updateText(selectedEntityProperties); - that.select(currentSelection, false); // TODO: this should be more than highlighted - }; - - that.rotateYaw = function(event) { - if (!entitySelected || mode !== "ROTATE_YAW") { - return; // not allowed - } - - var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); - - if (debug) { - print("rotateYaw()..."); - print(" event.x,y:" + event.x + "," + event.y); - } - - var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); - - var result = Overlays.findRayIntersection(pickRay); - - if (debug) { - print(" findRayIntersection() .... result.intersects:" + result.intersects); - } - - if (result.intersects) { - - + addGrabberTool(grabberMoveUp, { + mode: "TRANSLATE_UP_DOWN", + onBegin: function(event) { + SelectionManager.saveProperties(); + }, + onEnd: function(event, reason) { + if (reason == 'cancel') { + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID.id]; + Entities.editEntity(entityID, initialProperties); + } + } + }, + onMove: function(event) { + if (!entitySelected || mode !== "TRANSLATE_UP_DOWN") { + return; // not allowed + } + pickRay = Camera.computePickRay(event.x, event.y); + + // translate mode left/right based on view toward entity + var newIntersection = rayPlaneIntersection(pickRay, + selectedEntityPropertiesOriginalPosition, + Quat.getFront(lastCameraOrientation)); + + var vector = Vec3.subtract(newIntersection, lastPlaneIntersection); + lastPlaneIntersection = newIntersection; + + // we only care about the Y axis + vector.x = 0; + vector.z = 0; + + // newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, vector); + + var wantDebug = false; + if (wantDebug) { + print("translateUpDown... "); + print(" event.y:" + event.y); + Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); + Vec3.print(" newIntersection:", newIntersection); + Vec3.print(" vector:", vector); + Vec3.print(" originalPosition:", selectedEntityPropertiesOriginalPosition); + Vec3.print(" recentPosition:", selectedEntityProperties.position); + Vec3.print(" newPosition:", newPosition); + } + for (var i = 0; i < SelectionManager.selections.length; i++) { + var properties = Entities.getEntityProperties(SelectionManager.selections[i]); + var original = properties.position; + properties.position = Vec3.sum(properties.position, vector); + Entities.editEntity(SelectionManager.selections[i], properties); + } + + tooltip.updateText(selectedEntityProperties); + that.select(currentSelection, false); // TODO: this should be more than highlighted + SelectionManager._update(); + }, + }); + + var vec3Mult = function(v1, v2) { + return { x: v1.x * v2.x, y: v1.y * v2.y, z: v1.z * v2.z }; + } + var makeStretchTool = function(stretchMode, direction, pivot) { + var signs = { + x: direction.x < 0 ? -1 : (direction.x > 0 ? 1 : 0), + y: direction.y < 0 ? -1 : (direction.y > 0 ? 1 : 0), + z: direction.z < 0 ? -1 : (direction.z > 0 ? 1 : 0), + }; + + var mask = { + x: Math.abs(direction.x) > 0 ? 1 : 0, + y: Math.abs(direction.y) > 0 ? 1 : 0, + z: Math.abs(direction.z) > 0 ? 1 : 0, + }; + + var numDimensions = mask.x + mask.y + mask.z; + + var planeNormal = null; + var lastPick = null; + var initialPosition = null; + var initialDimensions = null; + var initialIntersection = null; + var initialProperties = null; + var rotation = null; + + var onBegin = function(event) { + print("STARTING: " + stretchMode); var properties = Entities.getEntityProperties(currentSelection); - var center = yawCenter; - var zero = yawZero; - var centerToZero = Vec3.subtract(center, zero); - var centerToIntersect = Vec3.subtract(center, result.intersection); - var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); - - var distanceFromCenter = Vec3.distance(center, result.intersection); - var snapToInner = false; - if (distanceFromCenter < innerRadius) { - angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; - snapToInner = true; - } - - // for debugging - if (debug) { - Vec3.print(" result.intersection:",result.intersection); - Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); - print(" angleFromZero:" + angleFromZero); + initialProperties = properties; + rotation = spaceMode == SPACE_LOCAL ? properties.rotation : Quat.fromPitchYawRollDegrees(0, 0, 0); + + if (spaceMode == SPACE_LOCAL) { + initialPosition = SelectionManager.localPosition; + initialDimensions = SelectionManager.localDimensions; + } else { + initialPosition = SelectionManager.worldPosition; + initialDimensions = SelectionManager.worldDimensions; } - var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); - var newRotation = Quat.multiply(yawChange, originalRotation); - - Entities.editEntity(currentSelection, { rotation: newRotation }); - - // update the rotation display accordingly... - var startAtCurrent = 0; - var endAtCurrent = angleFromZero; - var startAtRemainder = angleFromZero; - var endAtRemainder = 360; - if (angleFromZero < 0) { - startAtCurrent = 360 + angleFromZero; - endAtCurrent = 360; - startAtRemainder = 0; - endAtRemainder = startAtCurrent; + if (numDimensions == 1 && mask.x) { + var start = Vec3.multiplyQbyV(rotation, { x: -10000, y: 0, z: 0 }); + start = Vec3.sum(start, properties.position); + var end = Vec3.multiplyQbyV(rotation, { x: 10000, y: 0, z: 0 }); + end = Vec3.sum(end, properties.position); + Overlays.editOverlay(xRailOverlay, { + start: start, + end: end, + visible: true, + }); } - if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + if (numDimensions == 1 && mask.y) { + var start = Vec3.multiplyQbyV(rotation, { x: 0, y: -10000, z: 0 }); + start = Vec3.sum(start, properties.position); + var end = Vec3.multiplyQbyV(rotation, { x: 0, y: 10000, z: 0 }); + end = Vec3.sum(end, properties.position); + Overlays.editOverlay(yRailOverlay, { + start: start, + end: end, + visible: true, + }); + } + if (numDimensions == 1 && mask.z) { + var start = Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: -10000 }); + start = Vec3.sum(start, properties.position); + var end = Vec3.multiplyQbyV(rotation, { x: 0, y: 0, z: 10000 }); + end = Vec3.sum(end, properties.position); + Overlays.editOverlay(zRailOverlay, { + start: start, + end: end, + visible: true, + }); + } + if (numDimensions == 1) { + if (mask.x == 1) { + planeNormal = { x: 0, y: 1, z: 0 }; + } else if (mask.y == 1) { + planeNormal = { x: 1, y: 0, z: 0 }; + } else { + planeNormal = { x: 0, y: 1, z: 0 }; + } + } else if (numDimensions == 2) { + if (mask.x == 0) { + planeNormal = { x: 1, y: 0, z: 0 }; + } else if (mask.y == 0) { + planeNormal = { x: 0, y: 1, z: 0 }; + } else { + planeNormal = { x: 0, y: 0, z: z }; + } + } + planeNormal = Vec3.multiplyQbyV(rotation, planeNormal); + var pickRay = Camera.computePickRay(event.x, event.y); + lastPick = rayPlaneIntersection(pickRay, + initialPosition, + planeNormal); + + // Overlays.editOverlay(normalLine, { + // start: initialPosition, + // end: Vec3.sum(Vec3.multiply(100000, planeNormal), initialPosition), + // }); + + SelectionManager.saveProperties(); + }; + + var onEnd = function(event, reason) { + print("ENDING: " + stretchMode); + Overlays.editOverlay(xRailOverlay, { visible: false }); + Overlays.editOverlay(yRailOverlay, { visible: false }); + Overlays.editOverlay(zRailOverlay, { visible: false }); + + if (reason == 'cancel') { + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID.id]; + Entities.editEntity(entityID, initialProperties); + } + } + }; + + var onMove = function(event) { + if (!entitySelected || mode !== stretchMode) { + return; // not allowed + } + var proportional = spaceMode == SPACE_WORLD || event.isShifted; + + var position, dimensions, rotation; + if (spaceMode == SPACE_LOCAL) { + position = SelectionManager.localPosition; + dimensions = SelectionManager.localDimensions; + rotation = SelectionManager.localRotation; } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + position = SelectionManager.worldPosition; + dimensions = SelectionManager.worldDimensions; + rotation = SelectionManager.worldRotation; + } + + var pickRay = Camera.computePickRay(event.x, event.y); + newPick = rayPlaneIntersection(pickRay, + initialPosition, + planeNormal); + var vector = Vec3.subtract(newPick, lastPick); + + vector = Vec3.multiplyQbyV(Quat.inverse(rotation), vector); + + vector = vec3Mult(mask, vector); + + var changeInDimensions = Vec3.multiply(-1, vec3Mult(signs, vector)); + var newDimensions; + if (proportional) { + var absX = Math.abs(changeInDimensions.x); + var absY = Math.abs(changeInDimensions.y); + var absZ = Math.abs(changeInDimensions.z); + print('abs: ' + absX + ', ' + absY + ', ' + absZ); + var pctChange = 0; + if (absX > absY && absX > absZ) { + pctChange = changeInDimensions.x / initialProperties.dimensions.x; + pctChange = changeInDimensions.x / initialDimensions.x; + } else if (absY > absZ) { + pctChange = changeInDimensions.y / initialProperties.dimensions.y; + pctChange = changeInDimensions.y / initialDimensions.y; + } else { + pctChange = changeInDimensions.z / initialProperties.dimensions.z; + pctChange = changeInDimensions.z / initialDimensions.z; + } + print('change: ' + pctChange); + pctChange += 1.0; + newDimensions = Vec3.multiply(pctChange, initialDimensions); + } else { + newDimensions = Vec3.sum(initialDimensions, changeInDimensions); } + newDimensions.x = Math.max(newDimensions.x, MINIMUM_DIMENSION); + newDimensions.y = Math.max(newDimensions.y, MINIMUM_DIMENSION); + newDimensions.z = Math.max(newDimensions.z, MINIMUM_DIMENSION); + + var p = Vec3.multiply(0.5, pivot); + var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(p, changeInDimensions)); + var newPosition = Vec3.sum(initialPosition, changeInPosition); + + + selectedEntityProperties.position = newPosition; + selectedEntityProperties.dimensions = newDimensions; + for (var i = 0; i < SelectionManager.selections.length; i++) { + Entities.editEntity(SelectionManager.selections[i], selectedEntityProperties); + } + + var wantDebug = false; + if (wantDebug) { + print(stretchMode); + Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); + Vec3.print(" newIntersection:", newIntersection); + Vec3.print(" vector:", vector); + Vec3.print(" oldPOS:", oldPOS); + Vec3.print(" newPOS:", newPOS); + Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions); + Vec3.print(" changeInDimensions:", changeInDimensions); + Vec3.print(" newDimensions:", newDimensions); + + Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition); + Vec3.print(" changeInPosition:", changeInPosition); + Vec3.print(" newPosition:", newPosition); + } + + tooltip.updateText(selectedEntityProperties); + SelectionManager._update(); + that.select(currentSelection, false); // TODO: this should be more than highlighted + + }; + + return { + mode: stretchMode, + onBegin: onBegin, + onMove: onMove, + onEnd: onEnd + }; + }; + + that.cancelTool = function() { + if (activeTool) { + activeTool.onEnd(null, 'cancel'); + activeTool = null; + SelectionManager._update(); } }; - that.rotatePitch = function(event) { - if (!entitySelected || mode !== "ROTATE_PITCH") { - return; // not allowed + function addStretchTool(overlay, mode, pivot, direction) { + if (!pivot) { + pivot = Vec3.multiply(-1, direction); + pivot.y = direction.y; } - var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); + var tool = makeStretchTool(mode, direction, pivot); - if (debug) { - print("rotatePitch()..."); - print(" event.x,y:" + event.x + "," + event.y); - } + addGrabberTool(overlay, tool); + } - var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); - var result = Overlays.findRayIntersection(pickRay); + addStretchTool(grabberNEAR, "STRETCH_NEAR", { x: 0, y: 0, z: -1 }, { x: 0, y: 0, z: 1 }); + addStretchTool(grabberFAR, "STRETCH_FAR", { x: 0, y: 0, z: 1 }, { x: 0, y: 0, z: -1 }); + addStretchTool(grabberTOP, "STRETCH_TOP", { x: 0, y: 1, z: 0 }, { x: 0, y: -1, z: 0 }); + addStretchTool(grabberBOTTOM, "STRETCH_BOTTOM", { x: 0, y: -1, z: 0 }, { x: 0, y: 1, z: 0 }); + addStretchTool(grabberRIGHT, "STRETCH_RIGHT", { x: 1, y: 0, z: 0 }, { x: -1, y: 0, z: 0 }); + addStretchTool(grabberLEFT, "STRETCH_LEFT", { x: -1, y: 0, z: 0 }, { x: 1, y: 0, z: 0 }); - if (debug) { - print(" findRayIntersection() .... result.intersects:" + result.intersects); - } + addStretchTool(grabberLBN, "STRETCH_LBN", null, {x: 1, y: 0, z: 1}); + addStretchTool(grabberRBN, "STRETCH_RBN", null, {x: -1, y: 0, z: 1}); + addStretchTool(grabberLBF, "STRETCH_LBF", null, {x: 1, y: 0, z: -1}); + addStretchTool(grabberRBF, "STRETCH_RBF", null, {x: -1, y: 0, z: -1}); + addStretchTool(grabberLTN, "STRETCH_LTN", null, {x: 1, y: 0, z: 1}); + addStretchTool(grabberRTN, "STRETCH_RTN", null, {x: -1, y: 0, z: 1}); + addStretchTool(grabberLTF, "STRETCH_LTF", null, {x: 1, y: 0, z: -1}); + addStretchTool(grabberRTF, "STRETCH_RTF", null, {x: -1, y: 0, z: -1}); - if (result.intersects) { - var properties = Entities.getEntityProperties(currentSelection); - var center = pitchCenter; - var zero = pitchZero; - var centerToZero = Vec3.subtract(center, zero); - var centerToIntersect = Vec3.subtract(center, result.intersection); - var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + addStretchTool(grabberEdgeTR, "STRETCH_EdgeTR", null, {x: 1, y: 1, z: 0}); + addStretchTool(grabberEdgeTL, "STRETCH_EdgeTL", null, {x: -1, y: 1, z: 0}); + addStretchTool(grabberEdgeTF, "STRETCH_EdgeTF", null, {x: 0, y: 1, z: -1}); + addStretchTool(grabberEdgeTN, "STRETCH_EdgeTN", null, {x: 0, y: 1, z: 1}); + addStretchTool(grabberEdgeBR, "STRETCH_EdgeBR", null, {x: 1, y: -1, z: 0}); + addStretchTool(grabberEdgeBL, "STRETCH_EdgeBL", null, {x: -1, y: -1, z: 0}); + addStretchTool(grabberEdgeBF, "STRETCH_EdgeBF", null, {x: 0, y: -1, z: 1}); + addStretchTool(grabberEdgeBN, "STRETCH_EdgeBN", null, {x: 0, y: -1, z: -1}); + addStretchTool(grabberEdgeNR, "STRETCH_EdgeNR", null, {x: -1, y: 0, z: 1}); + addStretchTool(grabberEdgeNL, "STRETCH_EdgeNL", null, {x: 1, y: 0, z: 1}); + addStretchTool(grabberEdgeFR, "STRETCH_EdgeFR", null, {x: -1, y: 0, z: -1}); + addStretchTool(grabberEdgeFL, "STRETCH_EdgeFL", null, {x: 1, y: 0, z: -1}); - var distanceFromCenter = Vec3.distance(center, result.intersection); - var snapToInner = false; - if (distanceFromCenter < innerRadius) { - angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; - snapToInner = true; + var initialPosition = SelectionManager.worldPosition; + addGrabberTool(yawHandle, { + mode: "ROTATE_YAW", + onBegin: function(event) { + SelectionManager.saveProperties(); + initialPosition = SelectionManager.worldPosition; + }, + onEnd: function(event, reason) { + if (reason == 'cancel') { + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID.id]; + Entities.editEntity(entityID, initialProperties); + } + } + }, + onMove: function(event) { + if (!entitySelected || mode !== "ROTATE_YAW") { + return; // not allowed } - // for debugging + var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); + if (debug) { - Vec3.print(" result.intersection:",result.intersection); - Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); - print(" angleFromZero:" + angleFromZero); + print("rotateYaw()..."); + print(" event.x,y:" + event.x + "," + event.y); } - var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); - var newRotation = Quat.multiply(pitchChange, originalRotation); - - Entities.editEntity(currentSelection, { rotation: newRotation }); - - // update the rotation display accordingly... - var startAtCurrent = 0; - var endAtCurrent = angleFromZero; - var startAtRemainder = angleFromZero; - var endAtRemainder = 360; - if (angleFromZero < 0) { - startAtCurrent = 360 + angleFromZero; - endAtCurrent = 360; - startAtRemainder = 0; - endAtRemainder = startAtCurrent; - } - if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); - } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); - } - } - }; - - that.rotateRoll = function(event) { - if (!entitySelected || mode !== "ROTATE_ROLL") { - return; // not allowed - } - var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); - - if (debug) { - print("rotateRoll()..."); - print(" event.x,y:" + event.x + "," + event.y); - } - - var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); - var result = Overlays.findRayIntersection(pickRay); - - if (debug) { - print(" findRayIntersection() .... result.intersects:" + result.intersects); - } - - if (result.intersects) { - var properties = Entities.getEntityProperties(currentSelection); - var center = rollCenter; - var zero = rollZero; - var centerToZero = Vec3.subtract(center, zero); - var centerToIntersect = Vec3.subtract(center, result.intersection); - var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); - - var distanceFromCenter = Vec3.distance(center, result.intersection); - var snapToInner = false; - if (distanceFromCenter < innerRadius) { - angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; - snapToInner = true; - } - - // for debugging - if (debug) { - Vec3.print(" result.intersection:",result.intersection); - Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); - print(" angleFromZero:" + angleFromZero); - } - - var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); - var newRotation = Quat.multiply(rollChange, originalRotation); + var pickRay = Camera.computePickRay(event.x, event.y); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); + Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); - Entities.editEntity(currentSelection, { rotation: newRotation }); + var result = Overlays.findRayIntersection(pickRay); - // update the rotation display accordingly... - var startAtCurrent = 0; - var endAtCurrent = angleFromZero; - var startAtRemainder = angleFromZero; - var endAtRemainder = 360; - if (angleFromZero < 0) { - startAtCurrent = 360 + angleFromZero; - endAtCurrent = 360; - startAtRemainder = 0; - endAtRemainder = startAtCurrent; + if (debug) { + print(" findRayIntersection() .... result.intersects:" + result.intersects); } - if (snapToInner) { - Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, - majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, - majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); - } else { - Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); - Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); - Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, - majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, - majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + + if (result.intersects) { + var properties = Entities.getEntityProperties(currentSelection); + var center = yawCenter; + var zero = yawZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; + } + + // for debugging + if (debug) { + Vec3.print(" result.intersection:",result.intersection); + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); + print(" angleFromZero:" + angleFromZero); + } + + var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var properties = Entities.getEntityProperties(entityID); + var initialProperties = SelectionManager.savedProperties[entityID.id]; + var dPos = Vec3.subtract(initialProperties.position, initialPosition); + dPos = Vec3.multiplyQbyV(yawChange, dPos); + + Entities.editEntity(entityID, { + position: Vec3.sum(initialPosition, dPos), + rotation: Quat.multiply(yawChange, initialProperties.rotation), + }); + } + + // update the rotation display accordingly... + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + var startAtRemainder = angleFromZero; + var endAtRemainder = 360; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + startAtRemainder = 0; + endAtRemainder = startAtCurrent; + } + if (snapToInner) { + Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, + majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + } else { + Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, + majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + } + } } - }; + }); + + addGrabberTool(pitchHandle, { + mode: "ROTATE_PITCH", + onBegin: function(event) { + SelectionManager.saveProperties(); + initialPosition = SelectionManager.worldPosition; + }, + onEnd: function(event, reason) { + if (reason == 'cancel') { + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID.id]; + Entities.editEntity(entityID, initialProperties); + } + } + }, + onMove: function(event) { + if (!entitySelected || mode !== "ROTATE_PITCH") { + return; // not allowed + } + var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); + + if (debug) { + print("rotatePitch()..."); + print(" event.x,y:" + event.x + "," + event.y); + } + + var pickRay = Camera.computePickRay(event.x, event.y); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); + Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); + var result = Overlays.findRayIntersection(pickRay); + + if (debug) { + print(" findRayIntersection() .... result.intersects:" + result.intersects); + } + + if (result.intersects) { + var properties = Entities.getEntityProperties(currentSelection); + var center = pitchCenter; + var zero = pitchZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; + } + + // for debugging + if (debug) { + Vec3.print(" result.intersection:",result.intersection); + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); + print(" angleFromZero:" + angleFromZero); + } + + var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); + + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var properties = Entities.getEntityProperties(entityID); + var initialProperties = SelectionManager.savedProperties[entityID.id]; + var dPos = Vec3.subtract(initialProperties.position, initialPosition); + dPos = Vec3.multiplyQbyV(pitchChange, dPos); + + Entities.editEntity(entityID, { + position: Vec3.sum(initialPosition, dPos), + rotation: Quat.multiply(pitchChange, initialProperties.rotation), + }); + } + + // update the rotation display accordingly... + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + var startAtRemainder = angleFromZero; + var endAtRemainder = 360; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + startAtRemainder = 0; + endAtRemainder = startAtCurrent; + } + if (snapToInner) { + Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, + majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + } else { + Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, + majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + } + } + } + }); + + addGrabberTool(rollHandle, { + mode: "ROTATE_ROLL", + onBegin: function(event) { + SelectionManager.saveProperties(); + initialPosition = SelectionManager.worldPosition; + }, + onEnd: function(event, reason) { + if (reason == 'cancel') { + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var initialProperties = SelectionManager.savedProperties[entityID.id]; + Entities.editEntity(entityID, initialProperties); + } + } + }, + onMove: function(event) { + if (!entitySelected || mode !== "ROTATE_ROLL") { + return; // not allowed + } + var debug = Menu.isOptionChecked("Debug Ryans Rotation Problems"); + + if (debug) { + print("rotateRoll()..."); + print(" event.x,y:" + event.x + "," + event.y); + } + + var pickRay = Camera.computePickRay(event.x, event.y); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); + Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); + var result = Overlays.findRayIntersection(pickRay); + + if (debug) { + print(" findRayIntersection() .... result.intersects:" + result.intersects); + } + + if (result.intersects) { + var properties = Entities.getEntityProperties(currentSelection); + var center = rollCenter; + var zero = rollZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; + } + + // for debugging + if (debug) { + Vec3.print(" result.intersection:",result.intersection); + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); + print(" angleFromZero:" + angleFromZero); + } + + var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); + for (var i = 0; i < SelectionManager.selections.length; i++) { + var entityID = SelectionManager.selections[i]; + var properties = Entities.getEntityProperties(entityID); + var initialProperties = SelectionManager.savedProperties[entityID.id]; + var dPos = Vec3.subtract(initialProperties.position, initialPosition); + dPos = Vec3.multiplyQbyV(rollChange, dPos); + + Entities.editEntity(entityID, { + position: Vec3.sum(initialPosition, dPos), + rotation: Quat.multiply(rollChange, initialProperties.rotation), + }); + } + + // update the rotation display accordingly... + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + var startAtRemainder = angleFromZero; + var endAtRemainder = 360; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + startAtRemainder = 0; + endAtRemainder = startAtCurrent; + } + if (snapToInner) { + Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius, + majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, + majorTickMarksLength: -0.25, minorTickMarksLength: 0, }); + } else { + Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius, + majorTickMarksAngle: 45.0, minorTickMarksAngle: 5, + majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, }); + } + } + } + }); that.checkMove = function() { if (currentSelection.isKnownID && - (!Vec3.equal(MyAvatar.position, lastAvatarPosition) || !Quat.equal(MyAvatar.orientation, lastAvatarOrientation))){ - that.select(currentSelection, false); + (!Vec3.equal(Camera.getPosition(), lastCameraPosition) || !Quat.equal(Camera.getOrientation(), lastCameraOrientation))){ + that.select(currentSelection, false, false); } }; @@ -2011,119 +1828,66 @@ SelectionDisplay = (function () { Vec3.print(" result.intersection:", result.intersection); } - switch(result.overlayID) { - case grabberMoveUp: - mode = "TRANSLATE_UP_DOWN"; - somethingClicked = true; + var tool = grabberTools[result.overlayID]; + if (tool) { + print("FOUND TOOL! " + tool.mode); + activeTool = tool; + mode = tool.mode; + somethingClicked = true; + if (activeTool && activeTool.onBegin) { + activeTool.onBegin(event); + } + } else { + switch(result.overlayID) { + case grabberMoveUp: + mode = "TRANSLATE_UP_DOWN"; + somethingClicked = true; - // in translate mode, we hide our stretch handles... - Overlays.editOverlay(grabberLBN, { visible: false }); - Overlays.editOverlay(grabberLBF, { visible: false }); - Overlays.editOverlay(grabberRBN, { visible: false }); - Overlays.editOverlay(grabberRBF, { visible: false }); - Overlays.editOverlay(grabberLTN, { visible: false }); - Overlays.editOverlay(grabberLTF, { visible: false }); - Overlays.editOverlay(grabberRTN, { visible: false }); - Overlays.editOverlay(grabberRTF, { visible: false }); + // in translate mode, we hide our stretch handles... + for (var i = 0; i < stretchHandles.length; i++) { + Overlays.editOverlay(stretchHandles[i], { visible: false }); + } + break; - Overlays.editOverlay(grabberTOP, { visible: false }); - Overlays.editOverlay(grabberBOTTOM, { visible: false }); - Overlays.editOverlay(grabberLEFT, { visible: false }); - Overlays.editOverlay(grabberRIGHT, { visible: false }); - Overlays.editOverlay(grabberNEAR, { visible: false }); - Overlays.editOverlay(grabberFAR, { visible: false }); - Overlays.editOverlay(grabberEdgeTR, { visible: false }); - Overlays.editOverlay(grabberEdgeTL, { visible: false }); - Overlays.editOverlay(grabberEdgeTF, { visible: false }); - Overlays.editOverlay(grabberEdgeTN, { visible: false }); - Overlays.editOverlay(grabberEdgeBR, { visible: false }); - Overlays.editOverlay(grabberEdgeBL, { visible: false }); - Overlays.editOverlay(grabberEdgeBF, { visible: false }); - Overlays.editOverlay(grabberEdgeBN, { visible: false }); - Overlays.editOverlay(grabberEdgeNR, { visible: false }); - Overlays.editOverlay(grabberEdgeNL, { visible: false }); - Overlays.editOverlay(grabberEdgeFR, { visible: false }); - Overlays.editOverlay(grabberEdgeFL, { visible: false }); - break; + case grabberNEAR: + case grabberEdgeTN: // TODO: maybe this should be TOP+NEAR stretching? + case grabberEdgeBN: // TODO: maybe this should be BOTTOM+FAR stretching? + mode = "STRETCH_NEAR"; + somethingClicked = true; + break; - case grabberRBN: - mode = "STRETCH_RBN"; - somethingClicked = true; - break; + case grabberFAR: + case grabberEdgeTF: // TODO: maybe this should be TOP+FAR stretching? + case grabberEdgeBF: // TODO: maybe this should be BOTTOM+FAR stretching? + mode = "STRETCH_FAR"; + somethingClicked = true; + break; + case grabberTOP: + mode = "STRETCH_TOP"; + somethingClicked = true; + break; + case grabberBOTTOM: + mode = "STRETCH_BOTTOM"; + somethingClicked = true; + break; + case grabberRIGHT: + case grabberEdgeTR: // TODO: maybe this should be TOP+RIGHT stretching? + case grabberEdgeBR: // TODO: maybe this should be BOTTOM+RIGHT stretching? + mode = "STRETCH_RIGHT"; + somethingClicked = true; + break; + case grabberLEFT: + case grabberEdgeTL: // TODO: maybe this should be TOP+LEFT stretching? + case grabberEdgeBL: // TODO: maybe this should be BOTTOM+LEFT stretching? + mode = "STRETCH_LEFT"; + somethingClicked = true; + break; - case grabberLBN: - mode = "STRETCH_LBN"; - somethingClicked = true; - break; - - case grabberRTN: - mode = "STRETCH_RTN"; - somethingClicked = true; - break; - - case grabberLTN: - mode = "STRETCH_LTN"; - somethingClicked = true; - break; - - case grabberRBF: - mode = "STRETCH_RBF"; - somethingClicked = true; - break; - - case grabberLBF: - mode = "STRETCH_LBF"; - somethingClicked = true; - break; - - case grabberRTF: - mode = "STRETCH_RTF"; - somethingClicked = true; - break; - - case grabberLTF: - mode = "STRETCH_LTF"; - somethingClicked = true; - break; - - case grabberNEAR: - case grabberEdgeTN: // TODO: maybe this should be TOP+NEAR stretching? - case grabberEdgeBN: // TODO: maybe this should be BOTTOM+FAR stretching? - mode = "STRETCH_NEAR"; - somethingClicked = true; - break; - - case grabberFAR: - case grabberEdgeTF: // TODO: maybe this should be TOP+FAR stretching? - case grabberEdgeBF: // TODO: maybe this should be BOTTOM+FAR stretching? - mode = "STRETCH_FAR"; - somethingClicked = true; - break; - case grabberTOP: - mode = "STRETCH_TOP"; - somethingClicked = true; - break; - case grabberBOTTOM: - mode = "STRETCH_BOTTOM"; - somethingClicked = true; - break; - case grabberRIGHT: - case grabberEdgeTR: // TODO: maybe this should be TOP+RIGHT stretching? - case grabberEdgeBR: // TODO: maybe this should be BOTTOM+RIGHT stretching? - mode = "STRETCH_RIGHT"; - somethingClicked = true; - break; - case grabberLEFT: - case grabberEdgeTL: // TODO: maybe this should be TOP+LEFT stretching? - case grabberEdgeBL: // TODO: maybe this should be BOTTOM+LEFT stretching? - mode = "STRETCH_LEFT"; - somethingClicked = true; - break; - - default: - mode = "UNKNOWN"; - break; + default: + mode = "UNKNOWN"; + break; + } } } @@ -2164,6 +1928,16 @@ SelectionDisplay = (function () { originalRoll = roll; if (result.intersects) { + var tool = grabberTools[result.overlayID]; + if (tool) { + print("FOUND TOOL! " + tool.mode); + activeTool = tool; + mode = tool.mode; + somethingClicked = true; + if (activeTool && activeTool.onBegin) { + activeTool.onBegin(event); + } + } switch(result.overlayID) { case yawHandle: mode = "ROTATE_YAW"; @@ -2176,6 +1950,7 @@ SelectionDisplay = (function () { case pitchHandle: mode = "ROTATE_PITCH"; + initialPosition = SelectionManager.worldPosition; somethingClicked = true; overlayOrientation = pitchHandleRotation; overlayCenter = pitchCenter; @@ -2263,7 +2038,9 @@ SelectionDisplay = (function () { if (result.intersects) { switch(result.overlayID) { case selectionBox: - mode = "TRANSLATE_XZ"; + activeTool = translateXZTool; + mode = translateXZTool.mode; + activeTool.onBegin(event); somethingClicked = true; break; default: @@ -2277,7 +2054,7 @@ SelectionDisplay = (function () { if (somethingClicked) { pickRay = Camera.computePickRay(event.x, event.y); lastPlaneIntersection = rayPlaneIntersection(pickRay, selectedEntityPropertiesOriginalPosition, - Quat.getFront(lastAvatarOrientation)); + Quat.getFront(lastCameraOrientation)); if (wantDebug) { print("mousePressEvent()...... " + overlayNames[result.overlayID]); Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection); @@ -2296,169 +2073,111 @@ SelectionDisplay = (function () { }; that.mouseMoveEvent = function(event) { - //print("mouseMoveEvent()... mode:" + mode); - switch (mode) { - case "ROTATE_YAW": - that.rotateYaw(event); - break; - case "ROTATE_PITCH": - that.rotatePitch(event); - break; - case "ROTATE_ROLL": - that.rotateRoll(event); - break; - case "TRANSLATE_UP_DOWN": - that.translateUpDown(event); - break; - case "TRANSLATE_XZ": - that.translateXZ(event); - break; - case "STRETCH_RBN": - that.stretchRBN(event); - break; - case "STRETCH_LBN": - that.stretchLBN(event); - break; - case "STRETCH_RTN": - that.stretchRTN(event); - break; - case "STRETCH_LTN": - that.stretchLTN(event); - break; + if (activeTool) { + activeTool.onMove(event); + SelectionManager._update(); + return true; + } - case "STRETCH_RBF": - that.stretchRBF(event); - break; - case "STRETCH_LBF": - that.stretchLBF(event); - break; - case "STRETCH_RTF": - that.stretchRTF(event); - break; - case "STRETCH_LTF": - that.stretchLTF(event); - break; + // if no tool is active, then just look for handles to highlight... + var pickRay = Camera.computePickRay(event.x, event.y); + var result = Overlays.findRayIntersection(pickRay); + var pickedColor; + var pickedAlpha; + var highlightNeeded = false; - case "STRETCH_NEAR": - that.stretchNEAR(event); - break; - case "STRETCH_FAR": - that.stretchFAR(event); - break; - case "STRETCH_TOP": - that.stretchTOP(event); - break; - case "STRETCH_BOTTOM": - that.stretchBOTTOM(event); - break; - case "STRETCH_RIGHT": - that.stretchRIGHT(event); - break; - case "STRETCH_LEFT": - that.stretchLEFT(event); - break; - default: - // if not in any specific mode, then just look for handles to highlight... - var pickRay = Camera.computePickRay(event.x, event.y); - var result = Overlays.findRayIntersection(pickRay); - var pickedColor; - var pickedAlpha; - var highlightNeeded = false; - - if (result.intersects) { - switch(result.overlayID) { - case yawHandle: - case pitchHandle: - case rollHandle: - pickedColor = rotateHandleColor; - pickedAlpha = rotateHandleAlpha; - highlightNeeded = true; - break; - - case grabberMoveUp: - pickedColor = rotateHandleColor; - pickedAlpha = rotateHandleAlpha; - highlightNeeded = true; - break; - - case grabberLBN: - case grabberLBF: - case grabberRBN: - case grabberRBF: - case grabberLTN: - case grabberLTF: - case grabberRTN: - case grabberRTF: - pickedColor = grabberColorCorner; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - case grabberTOP: - case grabberBOTTOM: - case grabberLEFT: - case grabberRIGHT: - case grabberNEAR: - case grabberFAR: - pickedColor = grabberColorFace; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - case grabberEdgeTR: - case grabberEdgeTL: - case grabberEdgeTF: - case grabberEdgeTN: - case grabberEdgeBR: - case grabberEdgeBL: - case grabberEdgeBF: - case grabberEdgeBN: - case grabberEdgeNR: - case grabberEdgeNL: - case grabberEdgeFR: - case grabberEdgeFL: - pickedColor = grabberColorEdge; - pickedAlpha = grabberAlpha; - highlightNeeded = true; - break; - - default: - if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); - previousHandle = false; - } - break; - } + if (result.intersects) { + switch(result.overlayID) { + case yawHandle: + case pitchHandle: + case rollHandle: + pickedColor = rotateHandleColor; + pickedAlpha = rotateHandleAlpha; + highlightNeeded = true; + break; - if (highlightNeeded) { - if (previousHandle) { - Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); - previousHandle = false; - } - Overlays.editOverlay(result.overlayID, { color: highlightedHandleColor, alpha: highlightedHandleAlpha }); - previousHandle = result.overlayID; - previousHandleColor = pickedColor; - previousHandleAlpha = pickedAlpha; - } - - } else { + case grabberMoveUp: + pickedColor = rotateHandleColor; + pickedAlpha = rotateHandleAlpha; + highlightNeeded = true; + break; + + case grabberLBN: + case grabberLBF: + case grabberRBN: + case grabberRBF: + case grabberLTN: + case grabberLTF: + case grabberRTN: + case grabberRTF: + pickedColor = grabberColorCorner; + pickedAlpha = grabberAlpha; + highlightNeeded = true; + break; + + case grabberTOP: + case grabberBOTTOM: + case grabberLEFT: + case grabberRIGHT: + case grabberNEAR: + case grabberFAR: + pickedColor = grabberColorFace; + pickedAlpha = grabberAlpha; + highlightNeeded = true; + break; + + case grabberEdgeTR: + case grabberEdgeTL: + case grabberEdgeTF: + case grabberEdgeTN: + case grabberEdgeBR: + case grabberEdgeBL: + case grabberEdgeBF: + case grabberEdgeBN: + case grabberEdgeNR: + case grabberEdgeNL: + case grabberEdgeFR: + case grabberEdgeFL: + pickedColor = grabberColorEdge; + pickedAlpha = grabberAlpha; + highlightNeeded = true; + break; + + default: if (previousHandle) { Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); previousHandle = false; } + break; + } + + if (highlightNeeded) { + if (previousHandle) { + Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + previousHandle = false; } - - return false; + Overlays.editOverlay(result.overlayID, { color: highlightedHandleColor, alpha: highlightedHandleAlpha }); + previousHandle = result.overlayID; + previousHandleColor = pickedColor; + previousHandleAlpha = pickedAlpha; + } + + } else { + if (previousHandle) { + Overlays.editOverlay(previousHandle, { color: previousHandleColor, alpha: previousHandleAlpha }); + previousHandle = false; + } } - return true; + + return false; }; that.updateHandleSizes = function() { if (selectedEntityProperties) { var diff = Vec3.subtract(selectedEntityProperties.position, Camera.getPosition()); var grabberSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO; - for (var i = 0; i < cornerEdgeFaceGrabbers.length; i++) { - Overlays.editOverlay(cornerEdgeFaceGrabbers[i], { + for (var i = 0; i < stretchHandles.length; i++) { + Overlays.editOverlay(stretchHandles[i], { size: grabberSize, }); } @@ -2481,6 +2200,10 @@ SelectionDisplay = (function () { that.mouseReleaseEvent = function(event) { var showHandles = false; + if (activeTool && activeTool.onEnd) { + activeTool.onEnd(event); + } + activeTool = null; // hide our rotation overlays..., and show our handles if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL") { Overlays.editOverlay(rotateOverlayTarget, { visible: false }); diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index 756fbdb645..9b46cdaf3f 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -21,6 +21,7 @@ Script.include("libraries/progressDialog.js"); Script.include("libraries/entitySelectionTool.js"); var selectionDisplay = SelectionDisplay; +var selectionManager = SelectionManager; Script.include("libraries/ModelImporter.js"); var modelImporter = new ModelImporter(); @@ -34,6 +35,8 @@ var entityPropertyDialogBox = EntityPropertyDialogBox; Script.include("libraries/entityCameraTool.js"); var entityCameraTool = new EntityCameraTool(); +selectionManager.setEventListener(selectionDisplay.updateHandles()); + var windowDimensions = Controller.getViewportDimensions(); var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; var toolHeight = 50; @@ -440,6 +443,11 @@ function mousePressEvent(event) { orientation = MyAvatar.orientation; intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); + if (!event.isShifted) { + selectionManager.clearSelections(); + } + selectionManager.addEntity(foundEntity); + print("Model selected selectedEntityID:" + selectedEntityID.id); } @@ -641,14 +649,21 @@ Menu.menuItemEvent.connect(handeMenuEvent); Controller.keyReleaseEvent.connect(function (event) { // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items + print(event.text); if (event.text == "`") { handeMenuEvent("Edit Properties..."); } if (event.text == "BACKSPACE") { handeMenuEvent("Delete"); + } else if (event.text == "TAB") { + selectionDisplay.toggleSpaceMode(); + } else if (event.text == "ESC") { + selectionDisplay.cancelTool(); } else if (event.text == "f") { if (entitySelected) { - entityCameraTool.focus(selectedEntityProperties); + // Get latest properties + var properties = Entities.getEntityProperties(selectedEntityID); + entityCameraTool.focus(properties); } } }); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3a213cb64f..11ab5769cb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -45,6 +45,7 @@ #include "ui/AttachmentsDialog.h" #include "ui/InfoView.h" #include "ui/MetavoxelEditor.h" +#include "ui/MetavoxelNetworkSimulator.h" #include "ui/ModelsBrowser.h" #include "ui/LoginDialog.h" #include "ui/NodeBounds.h" @@ -431,6 +432,8 @@ Menu::Menu() : QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels"); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false, Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData())); + addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this, + SLOT(showMetavoxelNetworkSimulator())); QMenu* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false); @@ -1378,6 +1381,13 @@ void Menu::showMetavoxelEditor() { _MetavoxelEditor->raise(); } +void Menu::showMetavoxelNetworkSimulator() { + if (!_metavoxelNetworkSimulator) { + _metavoxelNetworkSimulator = new MetavoxelNetworkSimulator(); + } + _metavoxelNetworkSimulator->raise(); +} + void Menu::showScriptEditor() { if(!_ScriptEditor || !_ScriptEditor->isVisible()) { _ScriptEditor = new ScriptEditorWindow(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index a1936050ff..80f7f1e006 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -78,6 +78,7 @@ class AttachmentsDialog; class BandwidthDialog; class LodToolsDialog; class MetavoxelEditor; +class MetavoxelNetworkSimulator; class ChatWindow; class OctreeStatsDialog; class MenuItemProperties; @@ -218,6 +219,7 @@ private slots: void cycleFrustumRenderMode(); void runTests(); void showMetavoxelEditor(); + void showMetavoxelNetworkSimulator(); void showScriptEditor(); void showChat(); void toggleConsole(); @@ -274,6 +276,7 @@ private: FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; QPointer _MetavoxelEditor; + QPointer _metavoxelNetworkSimulator; QPointer _ScriptEditor; QPointer _chatWindow; QDialog* _jsConsole; @@ -430,6 +433,7 @@ namespace MenuOption { const QString MuteEnvironment = "Mute Environment"; const QString MyLocations = "My Locations..."; const QString NameLocation = "Name this location"; + const QString NetworkSimulator = "Network Simulator..."; const QString NewVoxelCullingMode = "New Voxel Culling Mode"; const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity"; const QString OctreeStats = "Voxel and Entity Statistics"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index e9cfde60f9..8166c3938c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -39,6 +39,15 @@ REGISTER_META_OBJECT(StaticModelRenderer) static int bufferPointVectorMetaTypeId = qRegisterMetaType(); +MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate, + int minimumDelay, int maximumDelay, int bandwidthLimit) : + dropRate(dropRate), + repeatRate(repeatRate), + minimumDelay(minimumDelay), + maximumDelay(maximumDelay), + bandwidthLimit(bandwidthLimit) { +} + void MetavoxelSystem::init() { MetavoxelClientManager::init(); DefaultMetavoxelRendererImplementation::init(); @@ -61,6 +70,16 @@ MetavoxelLOD MetavoxelSystem::getLOD() { return _lod; } +void MetavoxelSystem::setNetworkSimulation(const NetworkSimulation& simulation) { + QWriteLocker locker(&_networkSimulationLock); + _networkSimulation = simulation; +} + +MetavoxelSystem::NetworkSimulation MetavoxelSystem::getNetworkSimulation() { + QReadLocker locker(&_networkSimulationLock); + return _networkSimulation; +} + class SimulateVisitor : public MetavoxelVisitor { public: @@ -678,6 +697,28 @@ void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor, bool render) { } } +Throttle::Throttle() : + _limit(INT_MAX), + _total(0) { +} + +bool Throttle::shouldThrottle(int bytes) { + // clear expired buckets + qint64 now = QDateTime::currentMSecsSinceEpoch(); + while (!_buckets.isEmpty() && now >= _buckets.first().first) { + _total -= _buckets.takeFirst().second; + } + + // if possible, add the new bucket + if (_total + bytes > _limit) { + return true; + } + const int BUCKET_DURATION = 1000; + _buckets.append(Bucket(now + BUCKET_DURATION, bytes)); + _total += bytes; + return false; +} + MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) : MetavoxelClient(node, updater) { } @@ -692,10 +733,59 @@ MetavoxelData MetavoxelSystemClient::getAugmentedData() { return _augmentedData; } +class ReceiveDelayer : public QObject { +public: + + ReceiveDelayer(const SharedNodePointer& node, const QByteArray& packet); + +protected: + + virtual void timerEvent(QTimerEvent* event); + +private: + + SharedNodePointer _node; + QByteArray _packet; +}; + +ReceiveDelayer::ReceiveDelayer(const SharedNodePointer& node, const QByteArray& packet) : + _node(node), + _packet(packet) { +} + +void ReceiveDelayer::timerEvent(QTimerEvent* event) { + QMutexLocker locker(&_node->getMutex()); + MetavoxelClient* client = static_cast(_node->getLinkedData()); + if (client) { + QMetaObject::invokeMethod(&client->getSequencer(), "receivedDatagram", Q_ARG(const QByteArray&, _packet)); + } + deleteLater(); +} + int MetavoxelSystemClient::parseData(const QByteArray& packet) { // process through sequencer - QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); - Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size()); + MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); + if (randFloat() < simulation.dropRate) { + return packet.size(); + } + int count = (randFloat() < simulation.repeatRate) ? 2 : 1; + for (int i = 0; i < count; i++) { + if (simulation.bandwidthLimit > 0) { + _receiveThrottle.setLimit(simulation.bandwidthLimit); + if (_receiveThrottle.shouldThrottle(packet.size())) { + continue; + } + } + int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); + if (delay > 0) { + ReceiveDelayer* delayer = new ReceiveDelayer(_node, packet); + delayer->startTimer(delay); + + } else { + QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); + } + Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size()); + } return packet.size(); } @@ -774,9 +864,52 @@ void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { QThreadPool::globalInstance()->start(new Augmenter(_node, _data, getAugmentedData(), _remoteDataLOD)); } +class SendDelayer : public QObject { +public: + + SendDelayer(const SharedNodePointer& node, const QByteArray& data); + + virtual void timerEvent(QTimerEvent* event); + +private: + + SharedNodePointer _node; + QByteArray _data; +}; + +SendDelayer::SendDelayer(const SharedNodePointer& node, const QByteArray& data) : + _node(node), + _data(data.constData(), data.size()) { +} + +void SendDelayer::timerEvent(QTimerEvent* event) { + NodeList::getInstance()->writeDatagram(_data, _node); + deleteLater(); +} + void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { - NodeList::getInstance()->writeDatagram(data, _node); - Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); + MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); + if (randFloat() < simulation.dropRate) { + return; + } + int count = (randFloat() < simulation.repeatRate) ? 2 : 1; + for (int i = 0; i < count; i++) { + if (simulation.bandwidthLimit > 0) { + _sendThrottle.setLimit(simulation.bandwidthLimit); + if (_sendThrottle.shouldThrottle(data.size())) { + continue; + } + } + int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); + if (delay > 0) { + SendDelayer* delayer = new SendDelayer(_node, data); + delayer->startTimer(delay); + + } else { + NodeList::getInstance()->writeDatagram(data, _node); + } + Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); + } } BufferData::~BufferData() { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index ac6a75c68b..14a24eea59 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -31,12 +31,27 @@ class MetavoxelSystem : public MetavoxelClientManager { public: + class NetworkSimulation { + public: + float dropRate; + float repeatRate; + int minimumDelay; + int maximumDelay; + int bandwidthLimit; + + NetworkSimulation(float dropRate = 0.0f, float repeatRate = 0.0f, int minimumDelay = 0, + int maximumDelay = 0, int bandwidthLimit = 0); + }; + virtual void init(); virtual MetavoxelLOD getLOD(); const Frustum& getFrustum() const { return _frustum; } + void setNetworkSimulation(const NetworkSimulation& simulation); + NetworkSimulation getNetworkSimulation(); + const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; } const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; } const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; } @@ -93,6 +108,9 @@ private: MetavoxelLOD _lod; QReadWriteLock _lodLock; Frustum _frustum; + + NetworkSimulation _networkSimulation; + QReadWriteLock _networkSimulationLock; }; /// Generic abstract base class for objects that handle a signal. @@ -116,6 +134,28 @@ typedef QVector BufferPointVector; Q_DECLARE_METATYPE(BufferPointVector) +/// Simple throttle for limiting bandwidth on a per-second basis. +class Throttle { +public: + + Throttle(); + + /// Sets the per-second limit. + void setLimit(int limit) { _limit = limit; } + + /// Determines whether the message with the given size should be throttled (discarded). If not, registers the message + /// as having been processed (i.e., contributing to later throttling). + bool shouldThrottle(int bytes); + +private: + + int _limit; + int _total; + + typedef QPair Bucket; + QList _buckets; +}; + /// A client session associated with a single server. class MetavoxelSystemClient : public MetavoxelClient { Q_OBJECT @@ -145,6 +185,9 @@ private: MetavoxelData _augmentedData; MetavoxelData _renderedAugmentedData; QReadWriteLock _augmentedDataLock; + + Throttle _sendThrottle; + Throttle _receiveThrottle; }; /// Base class for cached static buffers. diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bf6f176d22..4751abfd84 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -296,6 +296,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { } else if (OculusManager::isConnected()) { estimatedPosition = OculusManager::getRelativePosition(); estimatedPosition.x *= -1.0f; + _trackedHeadPosition = estimatedPosition; const float OCULUS_LEAN_SCALE = 0.05f; estimatedPosition /= OCULUS_LEAN_SCALE; @@ -303,6 +304,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); if (tracker) { estimatedPosition = tracker->getHeadTranslation(); + _trackedHeadPosition = estimatedPosition; estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 98fc5ff74d..9cb9246da7 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -113,6 +113,7 @@ public: static void sendKillAvatar(); + Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; } Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); } Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); } Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); } @@ -237,6 +238,8 @@ private: RecorderPointer _recorder; + glm::vec3 _trackedHeadPosition; + // private methods void updateOrientation(float deltaTime); glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool walkingOnFloor); diff --git a/interface/src/ui/MetavoxelNetworkSimulator.cpp b/interface/src/ui/MetavoxelNetworkSimulator.cpp new file mode 100644 index 0000000000..aabee97636 --- /dev/null +++ b/interface/src/ui/MetavoxelNetworkSimulator.cpp @@ -0,0 +1,87 @@ +// +// MetavoxelNetworkSimulator.cpp +// interface/src/ui +// +// Created by Andrzej Kapolka on 10/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include + +#include "Application.h" +#include "MetavoxelNetworkSimulator.h" + +const int BYTES_PER_KILOBYTE = 1024; + +MetavoxelNetworkSimulator::MetavoxelNetworkSimulator() : + QWidget(Application::getInstance()->getGLWidget(), Qt::Dialog) { + + setWindowTitle("Metavoxel Network Simulator"); + setAttribute(Qt::WA_DeleteOnClose); + + QVBoxLayout* topLayout = new QVBoxLayout(); + setLayout(topLayout); + + QFormLayout* form = new QFormLayout(); + topLayout->addLayout(form); + + MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); + + form->addRow("Drop Rate:", _dropRate = new QDoubleSpinBox()); + _dropRate->setSuffix("%"); + _dropRate->setValue(simulation.dropRate * 100.0); + connect(_dropRate, static_cast(&QDoubleSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + + form->addRow("Repeat Rate:", _repeatRate = new QDoubleSpinBox()); + _repeatRate->setSuffix("%"); + _repeatRate->setValue(simulation.repeatRate * 100.0); + connect(_repeatRate, static_cast(&QDoubleSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + + form->addRow("Minimum Delay:", _minimumDelay = new QSpinBox()); + _minimumDelay->setMaximum(1000); + _minimumDelay->setSuffix("ms"); + _minimumDelay->setValue(simulation.minimumDelay); + connect(_minimumDelay, static_cast(&QSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + + form->addRow("Maximum Delay:", _maximumDelay = new QSpinBox()); + _maximumDelay->setMaximum(1000); + _maximumDelay->setSuffix("ms"); + _maximumDelay->setValue(simulation.maximumDelay); + connect(_maximumDelay, static_cast(&QSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + + form->addRow("Bandwidth Limit:", _bandwidthLimit = new QSpinBox()); + _bandwidthLimit->setMaximum(1024 * 1024); + _bandwidthLimit->setSuffix("KB/s"); + _bandwidthLimit->setValue(simulation.bandwidthLimit / BYTES_PER_KILOBYTE); + connect(_bandwidthLimit, static_cast(&QSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this); + topLayout->addWidget(buttons); + connect(buttons, &QDialogButtonBox::accepted, this, &QWidget::close); + + show(); +} + +void MetavoxelNetworkSimulator::updateMetavoxelSystem() { + int bandwidthLimit = _bandwidthLimit->value() * BYTES_PER_KILOBYTE; + if (bandwidthLimit > 0) { + // make sure the limit is enough to let at least one packet through + const int MINIMUM_BANDWIDTH_LIMIT = 2048; + bandwidthLimit = qMax(bandwidthLimit, MINIMUM_BANDWIDTH_LIMIT); + } + Application::getInstance()->getMetavoxels()->setNetworkSimulation(MetavoxelSystem::NetworkSimulation( + _dropRate->value() / 100.0, _repeatRate->value() / 100.0, qMin(_minimumDelay->value(), _maximumDelay->value()), + qMax(_minimumDelay->value(), _maximumDelay->value()), bandwidthLimit)); +} diff --git a/interface/src/ui/MetavoxelNetworkSimulator.h b/interface/src/ui/MetavoxelNetworkSimulator.h new file mode 100644 index 0000000000..18d0857ae4 --- /dev/null +++ b/interface/src/ui/MetavoxelNetworkSimulator.h @@ -0,0 +1,41 @@ +// +// MetavoxelNetworkSimulator.h +// interface/src/ui +// +// Created by Andrzej Kapolka on 10/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MetavoxelNetworkSimulator_h +#define hifi_MetavoxelNetworkSimulator_h + +#include + +class QDoubleSpinBox; +class QSpinBox; + +/// Allows tweaking network simulation (packet drop percentage, etc.) settings for metavoxels. +class MetavoxelNetworkSimulator : public QWidget { + Q_OBJECT + +public: + + MetavoxelNetworkSimulator(); + +private slots: + + void updateMetavoxelSystem(); + +private: + + QDoubleSpinBox* _dropRate; + QDoubleSpinBox* _repeatRate; + QSpinBox* _minimumDelay; + QSpinBox* _maximumDelay; + QSpinBox* _bandwidthLimit; +}; + +#endif // hifi_MetavoxelNetworkSimulator_h diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index e293bae5a9..fa750e4fe7 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -26,6 +26,7 @@ public: ~Base3DOverlay(); // getters + virtual bool is3D() const { return true; } const glm::vec3& getPosition() const { return _position; } const glm::vec3& getCenter() const { return _position; } // TODO: consider implementing registration points in this class float getLineWidth() const { return _lineWidth; } diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index dfa9b11d74..d4d0c40135 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -40,6 +40,7 @@ public: virtual void render() = 0; // getters + virtual bool is3D() const = 0; bool isLoaded() { return _isLoaded; } bool getVisible() const { return _visible; } xColor getColor(); diff --git a/interface/src/ui/overlays/Overlay2D.h b/interface/src/ui/overlays/Overlay2D.h index 34028de89a..283e7b7b23 100644 --- a/interface/src/ui/overlays/Overlay2D.h +++ b/interface/src/ui/overlays/Overlay2D.h @@ -29,6 +29,8 @@ class Overlay2D : public Overlay { public: Overlay2D(); ~Overlay2D(); + + virtual bool is3D() const { return false; } // getters int getX() const { return _bounds.x(); } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 922c6a0408..b079acb0a8 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -23,6 +23,7 @@ #include "Rectangle3DOverlay.h" #include "Sphere3DOverlay.h" #include "TextOverlay.h" +#include "Text3DOverlay.h" Overlays::Overlays() : _nextOverlayID(1) { } @@ -131,6 +132,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay = new ImageOverlay(); } else if (type == "text") { thisOverlay = new TextOverlay(); + } else if (type == "text3d") { + thisOverlay = new Text3DOverlay(); } else if (type == "cube") { thisOverlay = new Cube3DOverlay(); } else if (type == "sphere") { @@ -167,8 +170,7 @@ unsigned int Overlays::addOverlay(Overlay* overlay) { QWriteLocker lock(&_lock); unsigned int thisID = _nextOverlayID; _nextOverlayID++; - bool is3D = typeid(*overlay) != typeid(ImageOverlay) && typeid(*overlay) != typeid(TextOverlay); - if (is3D) { + if (overlay->is3D()) { _overlays3D[thisID] = overlay; } else { _overlays2D[thisID] = overlay; diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp new file mode 100644 index 0000000000..feb36943c3 --- /dev/null +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -0,0 +1,182 @@ +// +// Text3DOverlay.cpp +// interface/src/ui/overlays +// +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include "Application.h" +#include "Text3DOverlay.h" +#include "ui/TextRenderer.h" + +const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 }; +const float DEFAULT_MARGIN = 0.1f; + +Text3DOverlay::Text3DOverlay() : + _backgroundColor(DEFAULT_BACKGROUND_COLOR), + _lineHeight(0.1f), + _leftMargin(DEFAULT_MARGIN), + _topMargin(DEFAULT_MARGIN), + _rightMargin(DEFAULT_MARGIN), + _bottomMargin(DEFAULT_MARGIN), + _isFacingAvatar(false) +{ +} + +Text3DOverlay::~Text3DOverlay() { +} + +xColor Text3DOverlay::getBackgroundColor() { + if (_colorPulse == 0.0f) { + return _backgroundColor; + } + + float pulseLevel = updatePulse(); + xColor result = _backgroundColor; + if (_colorPulse < 0.0f) { + result.red *= (1.0f - pulseLevel); + result.green *= (1.0f - pulseLevel); + result.blue *= (1.0f - pulseLevel); + } else { + result.red *= pulseLevel; + result.green *= pulseLevel; + result.blue *= pulseLevel; + } + return result; +} + + +void Text3DOverlay::render() { + if (!_visible) { + return; // do nothing if we're not visible + } + + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + glm::quat rotation; + if (_isFacingAvatar) { + // rotate about vertical to face the camera + rotation = Application::getInstance()->getCamera()->getRotation(); + rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); + } else { + rotation = getRotation(); + } + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + + const float MAX_COLOR = 255.0f; + xColor backgroundColor = getBackgroundColor(); + float alpha = getAlpha(); + glColor4f(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, alpha); + + glm::vec2 dimensions = getDimensions(); + glm::vec2 halfDimensions = dimensions * 0.5f; + + const float SLIGHTLY_BEHIND = -0.005f; + + glBegin(GL_QUADS); + glVertex3f(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); + glVertex3f(halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); + glVertex3f(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); + glVertex3f(-halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); + glEnd(); + + const int FIXED_FONT_POINT_SIZE = 40; + const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation + + TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); + float LINE_SCALE_RATIO = 1.2f; + float maxHeight = (float)textRenderer->calculateHeight("Xy") * LINE_SCALE_RATIO; + + float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; + + glTranslatef(-(halfDimensions.x - _leftMargin), halfDimensions.y - _topMargin, 0.0f); + + glm::vec2 clipMinimum(0.0f, 0.0f); + glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor, + (dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor); + + glScalef(scaleFactor, -scaleFactor, 1.0); + enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipDimensions.x); + enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x); + enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipDimensions.y); + enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y); + + glColor3f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR); + QStringList lines = _text.split("\n"); + int lineOffset = maxHeight; + foreach(QString thisLine, lines) { + textRenderer->draw(0, lineOffset, qPrintable(thisLine)); + lineOffset += maxHeight; + } + + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + + } glPopMatrix(); + +} + +void Text3DOverlay::enableClipPlane(GLenum plane, float x, float y, float z, float w) { + GLdouble coefficients[] = { x, y, z, w }; + glClipPlane(plane, coefficients); + glEnable(plane); +} + +void Text3DOverlay::setProperties(const QScriptValue& properties) { + Planar3DOverlay::setProperties(properties); + + QScriptValue text = properties.property("text"); + if (text.isValid()) { + setText(text.toVariant().toString()); + } + + QScriptValue backgroundColor = properties.property("backgroundColor"); + if (backgroundColor.isValid()) { + QScriptValue red = backgroundColor.property("red"); + QScriptValue green = backgroundColor.property("green"); + QScriptValue blue = backgroundColor.property("blue"); + if (red.isValid() && green.isValid() && blue.isValid()) { + _backgroundColor.red = red.toVariant().toInt(); + _backgroundColor.green = green.toVariant().toInt(); + _backgroundColor.blue = blue.toVariant().toInt(); + } + } + + if (properties.property("lineHeight").isValid()) { + setLineHeight(properties.property("lineHeight").toVariant().toFloat()); + } + + if (properties.property("leftMargin").isValid()) { + setLeftMargin(properties.property("leftMargin").toVariant().toFloat()); + } + + if (properties.property("topMargin").isValid()) { + setTopMargin(properties.property("topMargin").toVariant().toFloat()); + } + + if (properties.property("rightMargin").isValid()) { + setRightMargin(properties.property("rightMargin").toVariant().toFloat()); + } + + if (properties.property("bottomMargin").isValid()) { + setBottomMargin(properties.property("bottomMargin").toVariant().toFloat()); + } + + + QScriptValue isFacingAvatarValue = properties.property("isFacingAvatar"); + if (isFacingAvatarValue.isValid()) { + _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); + } + +} + + diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h new file mode 100644 index 0000000000..855890e493 --- /dev/null +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -0,0 +1,61 @@ +// +// Text3DOverlay.h +// interface/src/ui/overlays +// +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Text3DOverlay_h +#define hifi_Text3DOverlay_h + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include "Planar3DOverlay.h" + +class Text3DOverlay : public Planar3DOverlay { + Q_OBJECT + +public: + Text3DOverlay(); + ~Text3DOverlay(); + virtual void render(); + + // getters + const QString& getText() const { return _text; } + float getLineHeight() const { return _lineHeight; } + float getLeftMargin() const { return _leftMargin; } + float getTopMargin() const { return _topMargin; } + float getRightMargin() const { return _rightMargin; } + float getBottomMargin() const { return _bottomMargin; } + xColor getBackgroundColor(); + + // setters + void setText(const QString& text) { _text = text; } + void setLineHeight(float value) { _lineHeight = value; } + void setLeftMargin(float margin) { _leftMargin = margin; } + void setTopMargin(float margin) { _topMargin = margin; } + void setRightMargin(float margin) { _rightMargin = margin; } + void setBottomMargin(float margin) { _bottomMargin = margin; } + + virtual void setProperties(const QScriptValue& properties); + +private: + void enableClipPlane(GLenum plane, float x, float y, float z, float w); + + QString _text; + xColor _backgroundColor; + float _lineHeight; + float _leftMargin; + float _topMargin; + float _rightMargin; + float _bottomMargin; + bool _isFacingAvatar; +}; + + +#endif // hifi_Text3DOverlay_h diff --git a/libraries/metavoxels/src/Endpoint.cpp b/libraries/metavoxels/src/Endpoint.cpp index 65e088c75e..5a4e74ce08 100644 --- a/libraries/metavoxels/src/Endpoint.cpp +++ b/libraries/metavoxels/src/Endpoint.cpp @@ -107,7 +107,8 @@ PacketRecord* Endpoint::maybeCreateReceiveRecord() const { return NULL; } -PacketRecord::PacketRecord(const MetavoxelLOD& lod, const MetavoxelData& data) : +PacketRecord::PacketRecord(int packetNumber, const MetavoxelLOD& lod, const MetavoxelData& data) : + _packetNumber(packetNumber), _lod(lod), _data(data) { } diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h index d6999196d8..816a1fe2a9 100644 --- a/libraries/metavoxels/src/Endpoint.h +++ b/libraries/metavoxels/src/Endpoint.h @@ -32,7 +32,7 @@ public: PacketRecord* baselineReceiveRecord = NULL); virtual ~Endpoint(); - const DatagramSequencer& getSequencer() const { return _sequencer; } + DatagramSequencer& getSequencer() { return _sequencer; } virtual void update(); @@ -45,10 +45,10 @@ protected slots: virtual void handleMessage(const QVariant& message, Bitstream& in); void recordSend(); - void recordReceive(); + virtual void recordReceive(); - void clearSendRecordsBefore(int index); - void clearReceiveRecordsBefore(int index); + virtual void clearSendRecordsBefore(int index); + virtual void clearReceiveRecordsBefore(int index); protected: @@ -71,14 +71,16 @@ protected: class PacketRecord { public: - PacketRecord(const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData()); + PacketRecord(int packetNumber = 0, const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData()); virtual ~PacketRecord(); + int getPacketNumber() const { return _packetNumber; } const MetavoxelLOD& getLOD() const { return _lod; } const MetavoxelData& getData() const { return _data; } private: + int _packetNumber; MetavoxelLOD _lod; MetavoxelData _data; }; diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index 57f7b53cc3..09164d72c4 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -216,12 +216,71 @@ void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) } } +PacketRecord* MetavoxelClient::getAcknowledgedSendRecord(int packetNumber) const { + PacketRecord* lastAcknowledged = getLastAcknowledgedSendRecord(); + if (lastAcknowledged->getPacketNumber() == packetNumber) { + return lastAcknowledged; + } + foreach (PacketRecord* record, _clearedSendRecords) { + if (record->getPacketNumber() == packetNumber) { + return record; + } + } + return NULL; +} + +PacketRecord* MetavoxelClient::getAcknowledgedReceiveRecord(int packetNumber) const { + PacketRecord* lastAcknowledged = getLastAcknowledgedReceiveRecord(); + if (lastAcknowledged->getPacketNumber() == packetNumber) { + return lastAcknowledged; + } + foreach (PacketRecord* record, _clearedReceiveRecords) { + if (record->getPacketNumber() == packetNumber) { + return record; + } + } + return NULL; +} + void MetavoxelClient::dataChanged(const MetavoxelData& oldData) { // make thread-safe copy QWriteLocker locker(&_dataCopyLock); _dataCopy = _data; } +void MetavoxelClient::recordReceive() { + Endpoint::recordReceive(); + + // clear the cleared lists + foreach (PacketRecord* record, _clearedSendRecords) { + delete record; + } + _clearedSendRecords.clear(); + + foreach (PacketRecord* record, _clearedReceiveRecords) { + delete record; + } + _clearedReceiveRecords.clear(); +} + +void MetavoxelClient::clearSendRecordsBefore(int index) { + // move to cleared list + QList::iterator end = _sendRecords.begin() + index + 1; + for (QList::const_iterator it = _sendRecords.begin(); it != end; it++) { + _clearedSendRecords.append(*it); + } + _sendRecords.erase(_sendRecords.begin(), end); +} + +void MetavoxelClient::clearReceiveRecordsBefore(int index) { + // move to cleared list + QList::iterator end = _receiveRecords.begin() + index + 1; + for (QList::const_iterator it = _receiveRecords.begin(); it != end; it++) { + _clearedReceiveRecords.append(*it); + } + _receiveRecords.erase(_receiveRecords.begin(), end); +} + void MetavoxelClient::writeUpdateMessage(Bitstream& out) { ClientStateMessage state = { _updater->getLOD() }; out << QVariant::fromValue(state); @@ -232,7 +291,9 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { if (userType == MetavoxelDeltaMessage::Type) { PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); if (_reliableDeltaChannel) { - _remoteData.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, _remoteDataLOD = _reliableDeltaLOD); + MetavoxelData reference = _remoteData; + MetavoxelLOD referenceLOD = _remoteDataLOD; + _remoteData.readDelta(reference, referenceLOD, in, _remoteDataLOD = _reliableDeltaLOD); _sequencer.getInputStream().persistReadMappings(in.getAndResetReadMappings()); in.clearPersistentMappings(); _reliableDeltaChannel = NULL; @@ -255,13 +316,22 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } } else if (userType == MetavoxelDeltaPendingMessage::Type) { // check the id to make sure this is not a delta we've already processed - int id = message.value().id; - if (id > _reliableDeltaID) { - _reliableDeltaID = id; + MetavoxelDeltaPendingMessage pending = message.value(); + if (pending.id > _reliableDeltaID) { + _reliableDeltaID = pending.id; _reliableDeltaChannel = _sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX); _reliableDeltaChannel->getBitstream().copyPersistentMappings(_sequencer.getInputStream()); - _reliableDeltaLOD = getLastAcknowledgedSendRecord()->getLOD(); - PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); + PacketRecord* sendRecord = getAcknowledgedSendRecord(pending.receivedPacketNumber); + if (!sendRecord) { + qWarning() << "Missing send record for delta" << pending.receivedPacketNumber; + return; + } + _reliableDeltaLOD = sendRecord->getLOD(); + PacketRecord* receiveRecord = getAcknowledgedReceiveRecord(pending.sentPacketNumber); + if (!receiveRecord) { + qWarning() << "Missing receive record for delta" << pending.sentPacketNumber; + return; + } _remoteDataLOD = receiveRecord->getLOD(); _remoteData = receiveRecord->getData(); } @@ -271,10 +341,11 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } PacketRecord* MetavoxelClient::maybeCreateSendRecord() const { - return new PacketRecord(_reliableDeltaChannel ? _reliableDeltaLOD : _updater->getLOD()); + return new PacketRecord(_sequencer.getOutgoingPacketNumber(), + _reliableDeltaChannel ? _reliableDeltaLOD : _updater->getLOD()); } PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const { - return new PacketRecord(_remoteDataLOD, _remoteData); + return new PacketRecord(_sequencer.getIncomingPacketNumber(), _remoteDataLOD, _remoteData); } diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index 7ddee2d68d..0a32f2986d 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -116,8 +116,16 @@ public: protected: + PacketRecord* getAcknowledgedSendRecord(int packetNumber) const; + PacketRecord* getAcknowledgedReceiveRecord(int packetNumber) const; + virtual void dataChanged(const MetavoxelData& oldData); + virtual void recordReceive(); + + virtual void clearSendRecordsBefore(int index); + virtual void clearReceiveRecordsBefore(int index); + virtual void writeUpdateMessage(Bitstream& out); virtual void handleMessage(const QVariant& message, Bitstream& in); @@ -132,9 +140,13 @@ protected: ReliableChannel* _reliableDeltaChannel; MetavoxelLOD _reliableDeltaLOD; int _reliableDeltaID; + QVariant _reliableDeltaMessage; MetavoxelData _dataCopy; QReadWriteLock _dataCopyLock; + + QList _clearedSendRecords; + QList _clearedReceiveRecords; }; #endif // hifi_MetavoxelClientManager_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 17379a930f..299ffbbb80 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -734,6 +734,22 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { } } } + + // if there are no visible colors, we can clear everything + bool foundOpaque = false; + for (const QRgb* src = colorContents.constData(), *end = src + colorContents.size(); src != end; src++) { + if (qAlpha(*src) != 0) { + foundOpaque = true; + break; + } + } + if (!foundOpaque) { + info.outputValues[0] = AttributeValue(_outputs.at(0)); + info.outputValues[1] = AttributeValue(_outputs.at(1)); + info.outputValues[2] = AttributeValue(_outputs.at(2)); + return STOP_RECURSION; + } + VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES)); info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), encodeInline(newColorPointer)); diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 9a7e254571..06f2fc9b8d 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -68,6 +68,8 @@ class MetavoxelDeltaPendingMessage { public: STREAM int id; + STREAM int sentPacketNumber; + STREAM int receivedPacketNumber; }; DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaPendingMessage) diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 283765c265..7f81a4c59e 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -81,7 +81,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 7; + return 8; case PacketTypeVoxelData: return VERSION_VOXELS_HAS_FILE_BREAKS; default: diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index f88df3b7c0..bab63bbd4e 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -58,6 +58,10 @@ glm::vec3 Vec3::normalize(const glm::vec3& v) { return glm::normalize(v); } +glm::vec3 Vec3::mix(const glm::vec3& v1, const glm::vec3& v2, float m) { + return glm::mix(v1, v2, m); +} + void Vec3::print(const QString& lable, const glm::vec3& v) { qDebug() << qPrintable(lable) << v.x << "," << v.y << "," << v.z; } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 693fd604f7..3f7d5476a2 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -36,6 +36,7 @@ public slots: float distance(const glm::vec3& v1, const glm::vec3& v2); float orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3); glm::vec3 normalize(const glm::vec3& v); + glm::vec3 mix(const glm::vec3& v1, const glm::vec3& v2, float m); void print(const QString& lable, const glm::vec3& v); bool equal(const glm::vec3& v1, const glm::vec3& v2); }; diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 0a6a5de96d..92f8d0568c 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -603,31 +603,27 @@ int RandomVisitor::visit(MetavoxelInfo& info) { class TestSendRecord : public PacketRecord { public: - TestSendRecord(const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData(), - const SharedObjectPointer& localState = SharedObjectPointer(), int packetNumber = 0); + TestSendRecord(int packetNumber = 0, const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData(), + const SharedObjectPointer& localState = SharedObjectPointer()); const SharedObjectPointer& getLocalState() const { return _localState; } - int getPacketNumber() const { return _packetNumber; } private: SharedObjectPointer _localState; - int _packetNumber; - }; -TestSendRecord::TestSendRecord(const MetavoxelLOD& lod, const MetavoxelData& data, - const SharedObjectPointer& localState, int packetNumber) : - PacketRecord(lod, data), - _localState(localState), - _packetNumber(packetNumber) { +TestSendRecord::TestSendRecord(int packetNumber, const MetavoxelLOD& lod, const MetavoxelData& data, + const SharedObjectPointer& localState) : + PacketRecord(packetNumber, lod, data), + _localState(localState) { } class TestReceiveRecord : public PacketRecord { public: - TestReceiveRecord(const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData(), - const SharedObjectPointer& remoteState = SharedObjectPointer()); + TestReceiveRecord(int packetNumber = 0, const MetavoxelLOD& lod = MetavoxelLOD(), + const MetavoxelData& data = MetavoxelData(), const SharedObjectPointer& remoteState = SharedObjectPointer()); const SharedObjectPointer& getRemoteState() const { return _remoteState; } @@ -636,9 +632,9 @@ private: SharedObjectPointer _remoteState; }; -TestReceiveRecord::TestReceiveRecord(const MetavoxelLOD& lod, +TestReceiveRecord::TestReceiveRecord(int packetNumber, const MetavoxelLOD& lod, const MetavoxelData& data, const SharedObjectPointer& remoteState) : - PacketRecord(lod, data), + PacketRecord(packetNumber, lod, data), _remoteState(remoteState) { } @@ -1110,14 +1106,14 @@ void TestEndpoint::handleMessage(const QVariant& message, Bitstream& in) { PacketRecord* TestEndpoint::maybeCreateSendRecord() const { if (_reliableDeltaChannel) { - return new TestSendRecord(_reliableDeltaLOD, _reliableDeltaData, _localState, _sequencer.getOutgoingPacketNumber()); + return new TestSendRecord(_sequencer.getOutgoingPacketNumber(), _reliableDeltaLOD, _reliableDeltaData, _localState); } - return new TestSendRecord(_lod, (_mode == METAVOXEL_CLIENT_MODE) ? MetavoxelData() : _data, - _localState, _sequencer.getOutgoingPacketNumber()); + return new TestSendRecord(_sequencer.getOutgoingPacketNumber(), _lod, + (_mode == METAVOXEL_CLIENT_MODE) ? MetavoxelData() : _data, _localState); } PacketRecord* TestEndpoint::maybeCreateReceiveRecord() const { - return new TestReceiveRecord(_remoteDataLOD, _remoteData, _remoteState); + return new TestReceiveRecord(_sequencer.getIncomingPacketNumber(), _remoteDataLOD, _remoteData, _remoteState); } void TestEndpoint::handleHighPriorityMessage(const QVariant& message) {