From 38f0bc9f8df06b8e5f4a2c60419aad18de3e4f2c Mon Sep 17 00:00:00 2001 From: DaveDubUK Date: Thu, 2 Oct 2014 07:47:20 +0100 Subject: [PATCH 01/50] Adding followThroughOverlappingAction script The purpose of this project is to validate the principles of animation into a procedural animation script. To test the theory, we implemented a simple procedural arm swing animation that is both realtime and that incorporates Disney's principles of animation (principle of Overlapping Action and Follow Through). When applied to the arm swing, the principles make the shoulder rotation lead the elbow rotation which will lead the wrist rotation. Doing so results in an organic and life-like effect. --- examples/followThroughOverlappingAction.js | 398 +++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 examples/followThroughOverlappingAction.js diff --git a/examples/followThroughOverlappingAction.js b/examples/followThroughOverlappingAction.js new file mode 100644 index 0000000000..6ae910fa77 --- /dev/null +++ b/examples/followThroughOverlappingAction.js @@ -0,0 +1,398 @@ + // +// follow-through-and-overlapping-action.js +// +// Simple demonstration showing the visual effect of adding the +// follow through and overlapping action technique to avatar movement. +// +// Designed and created by Ozan Serim and Davedub, August 2014 +// +// Version 1.0 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +// always at the top... +var pathToOverlays = "https://s3-us-west-1.amazonaws.com/highfidelity-public/ArmSwingScript/ArmSwingOverlays/"; + +// animation +var LEFT = 1; +var RIGHT = 2; +var DIRECTION = 0; + +// animation effect variables +var handEffect = 3.4; // 0 to jointEffectMax +var forearmEffect = 2.5; // 0 to jointEffectMax +var limbWeight = 8; // will only use nearest integer, as defines an array length +var effectStrength = 1; // 0 to 1 - overall effect strength +var overShoot = false; // false uses upper arm as driver for forearm and hand, true uses upper arm for forearm and lower arm as driver for hand. + +// min max +var weightMin = 1; +var weightMax = 20; +var jointEffectMax = 5; + +// animate self (tap the 'r' key) +var animateSelf = false; +var selfAnimateFrequency = 7.5; + +// overlay values +var handleValue = 0; +var controlPosX = Window.innerWidth/2 - 500; +var controlPosY = 0; +var minSliderX = controlPosX+18; +var sliderRangeX = 190; +var minHandleX = controlPosX-50; +var handleRangeX = 350/2; +var handlePosition = 0; + +// background overlay +var controllerBackground = Overlays.addOverlay("image", { + bounds: { x: controlPosX, y: controlPosY, width: 250, height: 380}, + imageURL: pathToOverlays+"flourish-augmentation-control-overlay.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); +var controllerRadioSelectedBackground = Overlays.addOverlay("image", { + bounds: { x: controlPosX, y: controlPosY, width: 250, height: 380}, + imageURL: pathToOverlays+"flourish-augmentation-control-radio-selected-overlay.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1, + visible: false + }); +// handle overlay +var applyMotionHandle = Overlays.addOverlay("image", { + bounds: { x: minHandleX+handleRangeX-39, y: controlPosY+232, width: 79, height: 100}, + imageURL: pathToOverlays+"flourish-augmentation-handle-overlay.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); +// slider overlays +var handEffectSlider = Overlays.addOverlay("image", { + bounds: { x: minSliderX + (handEffect / jointEffectMax * sliderRangeX), y: controlPosY+46, width: 25, height: 25}, + imageURL: pathToOverlays+"ddao-slider-handle.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); +var forearmEffectSlider = Overlays.addOverlay("image", { + bounds: { x: minSliderX + (forearmEffect / jointEffectMax * sliderRangeX), y: controlPosY+86, width: 25, height: 25}, + imageURL: pathToOverlays+"ddao-slider-handle.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); +var limbWeightSlider = Overlays.addOverlay("image", { + bounds: { x: minSliderX + (limbWeight / weightMax * sliderRangeX), y: controlPosY+126, width: 25, height: 25}, + imageURL: pathToOverlays+"ddao-slider-handle.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); +var effectStrengthSlider = Overlays.addOverlay("image", { + bounds: { x: minSliderX + (effectStrength * sliderRangeX), y: controlPosY+206, width: 25, height: 25}, + imageURL: pathToOverlays+"ddao-slider-handle.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + + +// main loop - using averaging filters to add limb element follow-through +var upperArmPDampingFilter = []; +upperArmPDampingFilter.length = parseInt(limbWeight); // sets amount of damping for upper arm pitch +var forearmPDampingFilter = []; +forearmPDampingFilter.length = parseInt(limbWeight) + 2; // sets amount of damping for lower arm pitch + +var cumulativeTime = 0; + +Script.update.connect(function(deltaTime) { + + // used for self animating (press r to invoke) + cumulativeTime += deltaTime; + + // blend three keyframes using handle position to determine progress between keyframes + var animationProgress = handleValue; + + if(animateSelf) { + animationProgress = Math.sin(cumulativeTime * selfAnimateFrequency); + animationProgress++; + animationProgress/=2; + } + + var keyframeOneWeight = 0; + var keyframeTwoWeight = 0; + var keyframeThreeWeight = 0; + + if(movingHandle || animateSelf) { + keyframeOneWeight = 0; + keyframeTwoWeight = animationProgress; + keyframeThreeWeight = 1-animationProgress; + } + else if(!movingHandle) { + // idle + keyframeOneWeight = 1; + keyframeTwoWeight = 0; + keyframeThreeWeight = 0; + } + + var shoulderPitch = + keyframeOneWeight * keyFrameOne.joints[8].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[8].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[8].pitchOffset; + + var upperArmPitch = + keyframeOneWeight * keyFrameOne.joints[9].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[9].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[9].pitchOffset; + + // get the change in upper arm pitch and use to add weight effect to forearm (always) and hand (only for overShoot 1) + var deltaUpperArmPitch = effectStrength * (upperArmPDampingFilter[upperArmPDampingFilter.length-1] - upperArmPDampingFilter[0]); + + var forearmPitch = + keyframeOneWeight * keyFrameOne.joints[10].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[10].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[10].pitchOffset - + (deltaUpperArmPitch/(jointEffectMax - forearmEffect)); + + // there are two methods for calculating the hand follow through + var handPitch = 0; + if(overShoot) { + // get the change in forearm pitch and use to add weight effect to hand + var deltaForearmPitch = effectStrength * (forearmPDampingFilter[forearmPDampingFilter.length-1] - forearmPDampingFilter[0]); + handPitch = + keyframeOneWeight * keyFrameOne.joints[11].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[11].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[11].pitchOffset + + (deltaForearmPitch/(jointEffectMax - handEffect)); // driven by forearm + } else { + handPitch = + keyframeOneWeight * keyFrameOne.joints[11].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[11].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[11].pitchOffset - + (deltaUpperArmPitch/(jointEffectMax - handEffect)); // driven by upper arm + } + + var shoulderYaw = + keyframeOneWeight * keyFrameOne.joints[8].yawOffset + + keyframeTwoWeight * keyFrameTwo.joints[8].yawOffset + + keyframeThreeWeight * keyFrameThree.joints[8].yawOffset; + + var upperArmYaw = + keyframeOneWeight * keyFrameOne.joints[9].yawOffset + + keyframeTwoWeight * keyFrameTwo.joints[9].yawOffset + + keyframeThreeWeight * keyFrameThree.joints[9].yawOffset; + + var lowerArmYaw = + keyframeOneWeight * keyFrameOne.joints[10].yawOffset + + keyframeTwoWeight * keyFrameTwo.joints[10].yawOffset + + keyframeThreeWeight * keyFrameThree.joints[10].yawOffset; + + var handYaw = + keyframeOneWeight * keyFrameOne.joints[11].yawOffset + + keyframeTwoWeight * keyFrameTwo.joints[11].yawOffset + + keyframeThreeWeight * keyFrameThree.joints[11].yawOffset; + + var shoulderRoll = + keyframeOneWeight * keyFrameOne.joints[8].rollOffset + + keyframeTwoWeight * keyFrameTwo.joints[8].rollOffset + + keyframeThreeWeight * keyFrameThree.joints[8].rollOffset; + + var upperArmRoll = + keyframeOneWeight * keyFrameOne.joints[9].rollOffset + + keyframeTwoWeight * keyFrameTwo.joints[9].rollOffset + + keyframeThreeWeight * keyFrameThree.joints[9].rollOffset; + + var lowerArmRoll = + keyframeOneWeight * keyFrameOne.joints[10].rollOffset + + keyframeTwoWeight * keyFrameTwo.joints[10].rollOffset + + keyframeThreeWeight * keyFrameThree.joints[10].rollOffset; + + var handRoll = + keyframeOneWeight * keyFrameOne.joints[11].rollOffset + + keyframeTwoWeight * keyFrameTwo.joints[11].rollOffset + + keyframeThreeWeight * keyFrameThree.joints[11].rollOffset; + + // filter upper arm pitch + upperArmPDampingFilter.push(upperArmPitch); + upperArmPDampingFilter.shift(); + var upperArmPitchFiltered = 0; + for(ea in upperArmPDampingFilter) upperArmPitchFiltered += upperArmPDampingFilter[ea]; + upperArmPitchFiltered /= upperArmPDampingFilter.length; + upperArmPitch = (effectStrength*upperArmPitchFiltered) + ((1-(effectStrength)) * upperArmPitch); + + // filter forearm pitch only if using for hand follow-though + if(overShoot) { + forearmPDampingFilter.push(forearmPitch); + forearmPDampingFilter.shift(); + var forearmPitchFiltered = 0; + for(ea in forearmPDampingFilter) forearmPitchFiltered += forearmPDampingFilter[ea]; + forearmPitchFiltered /= forearmPDampingFilter.length; + forearmPitch = (effectStrength*forearmPitchFiltered) + ((1-(effectStrength)) * forearmPitch); + } + + // apply the new rotation data to the joints + MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees( shoulderPitch, shoulderYaw, shoulderRoll)); + MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees( upperArmPitch, -upperArmYaw, upperArmRoll)); + MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees( forearmPitch, lowerArmYaw, lowerArmRoll)); + MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollDegrees( handPitch, handYaw, handRoll)); +}); + + +// mouse handling +var movingHandEffectSlider = false; +var movingForearmEffectSlider = false; +var movingLimbWeightSlider = false; +var movingDampingSlider = false; +var movingEffectStrengthSlider = false; +var movingHandle = false; + +function mousePressEvent(event) { + + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + + if(clickedOverlay===applyMotionHandle) movingHandle = true; + else if(clickedOverlay===handEffectSlider) movingHandEffectSlider = true; + else if(clickedOverlay===forearmEffectSlider) movingForearmEffectSlider = true; + else if(clickedOverlay===limbWeightSlider) movingLimbWeightSlider = true; + else if(clickedOverlay===effectStrengthSlider) movingEffectStrengthSlider = true; + else if(clickedOverlay===controllerRadioSelectedBackground && + event.x>477&&event.x<497&&event.y>338&&event.y<360) { + overShoot = false; + Overlays.editOverlay(controllerBackground, {visible:true}); + Overlays.editOverlay(controllerRadioSelectedBackground, {visible:false}); + } + else if(clickedOverlay===controllerBackground && + event.x>477&&event.x<497&&event.y>338&&event.y<360){ + overShoot = true; + Overlays.editOverlay(controllerBackground, {visible:false}); + Overlays.editOverlay(controllerRadioSelectedBackground, {visible:true}); + } +} +function mouseMoveEvent(event) { + + if(movingHandle) { + var thumbClickOffsetX = event.x - minHandleX; + var thumbPositionNormalised = (thumbClickOffsetX - handleRangeX + 39) / handleRangeX; + if(thumbPositionNormalised<=-1) thumbPositionNormalised = -1; + else if(thumbPositionNormalised>1) thumbPositionNormalised = 1; + + if(thumbPositionNormalised<0) DIRECTION = LEFT; + else DIRECTION = RIGHT; + + handleValue = (thumbPositionNormalised+1)/2; + var handleX = (thumbPositionNormalised * handleRangeX) + handleRangeX - 39; // sets range + Overlays.editOverlay(applyMotionHandle, { x: handleX + minHandleX} ); + return; + } + else if(movingHandEffectSlider) { + var thumbClickOffsetX = event.x - minSliderX; + var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; + if(thumbPositionNormalised<0) thumbPositionNormalised = 0; + if(thumbPositionNormalised>1) thumbPositionNormalised = 1; + handEffect = (thumbPositionNormalised-0.08) * jointEffectMax; + var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range + Overlays.editOverlay(handEffectSlider, { x: sliderX + minSliderX} ); + } + else if(movingForearmEffectSlider) { + var thumbClickOffsetX = event.x - minSliderX; + var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; + if(thumbPositionNormalised<0) thumbPositionNormalised = 0; + if(thumbPositionNormalised>1) thumbPositionNormalised = 1; + forearmEffect = (thumbPositionNormalised-0.1) * jointEffectMax; + var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range + Overlays.editOverlay(forearmEffectSlider, { x: sliderX + minSliderX} ); + } + else if(movingLimbWeightSlider) { + var thumbClickOffsetX = event.x - minSliderX; + var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; + if(thumbPositionNormalised<0) thumbPositionNormalised = 0; + if(thumbPositionNormalised>1) thumbPositionNormalised = 1; + limbWeight = thumbPositionNormalised * weightMax; + if(limbWeight1) thumbPositionNormalised = 1; + effectStrength = thumbPositionNormalised; + var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range + Overlays.editOverlay(effectStrengthSlider, { x: sliderX + minSliderX} ); + return; + } +} +function mouseReleaseEvent(event) { + + if(movingHandle) { + movingHandle = false; + handleValue = 0; + Overlays.editOverlay(applyMotionHandle, { x: minHandleX+handleRangeX-39} ); + } + else if(movingHandEffectSlider) movingHandEffectSlider = false; + else if(movingForearmEffectSlider) movingForearmEffectSlider = false; + else if(movingLimbWeightSlider) movingLimbWeightSlider = false; + else if(movingEffectStrengthSlider) movingEffectStrengthSlider = false; + else if(movingDampingSlider) movingDampingSlider = false; +} + +// set up mouse and keyboard callbacks +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); +Controller.keyPressEvent.connect(keyPressEvent); + +// keyboard command +function keyPressEvent(event) { + + if (event.text == "q") { + print('hand effect = ' + handEffect + '\n'); + print('forearmEffect = ' + forearmEffect + '\n'); + print('limbWeight = ' + limbWeight + '\n'); + print('effectStrength = ' + effectStrength + '\n'); + } + else if (event.text == "r") { + animateSelf = !animateSelf; + } + else if (event.text == "[") { + selfAnimateFrequency+=0.5 + print('selfAnimateFrequency = '+selfAnimateFrequency); + } + else if (event.text == "]") { + selfAnimateFrequency-=0.5 + print('selfAnimateFrequency = '+selfAnimateFrequency); + } +} + + +// zero out all joints +function resetJoints() { + + var avatarJointNames = MyAvatar.getJointNames(); + for (var i = 0; i < avatarJointNames.length; i++) { + MyAvatar.clearJointData(avatarJointNames[i]); + } +} + +// Script ending +Script.scriptEnding.connect(function() { + + // delete the overlays + Overlays.deleteOverlay(controllerBackground); + Overlays.deleteOverlay(controllerRadioSelectedBackground); + Overlays.deleteOverlay(handEffectSlider); + Overlays.deleteOverlay(forearmEffectSlider); + Overlays.deleteOverlay(limbWeightSlider); + Overlays.deleteOverlay(effectStrengthSlider); + Overlays.deleteOverlay(applyMotionHandle); + + // leave the avi in zeroed out stance + resetJoints(); +}); + + +// animation / joint data. animation keyframes produced using davedub's procedural animation controller +MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(80,0,0)); // stand nicely please +var keyFrameOne = {"name":"FemaleStandingOne","settings":{"baseFrequency":70,"flyingHipsPitch":60,"takeFlightVelocity":40,"maxBankingAngle":40},"adjusters":{"legsSeparation":{"strength":-0.03679245283018867,"separationAngle":50},"stride":{"strength":0,"upperLegsPitch":30,"lowerLegsPitch":15,"upperLegsPitchOffset":0.2,"lowerLegsPitchOffset":1.5}},"joints":[{"name":"hips","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0,"thrust":0,"bob":0,"sway":0,"thrustPhase":180,"bobPhase":0,"swayPhase":-90,"thrustOffset":0,"bobOffset":0,"swayOffset":0},{"name":"upperLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"lowerLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"feet","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"toes","pitch":2.0377358490566038,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":4.415094339622641,"yawOffset":0,"rollOffset":0},{"name":"spine","pitch":1.660377358490566,"yaw":0,"roll":0,"pitchPhase":-180,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine1","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine2","pitch":2.1132075471698113,"yaw":0,"roll":0,"pitchPhase":-0.6792452830188722,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"shoulders","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0.6792452830188678,"yawOffset":-5.20754716981132,"rollOffset":-2.9433962264150937},{"name":"upperArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":77.77358490566039,"yawOffset":9.169811320754715,"rollOffset":0},{"name":"lowerArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"hands","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":1.6981132075471694,"yawOffset":-1.0188679245283017,"rollOffset":1.0188679245283017},{"name":"head","pitch":0,"yaw":1.7358490566037734,"roll":1.5094339622641508,"pitchPhase":-90.33962264150944,"yawPhase":94.41509433962267,"rollPhase":0,"pitchOffset":1.6981132075471694,"yawOffset":0,"rollOffset":0}]}; +var keyFrameTwo = {"name":"FemaleStandingOne","settings":{"baseFrequency":70,"flyingHipsPitch":60,"takeFlightVelocity":40,"maxBankingAngle":40},"adjusters":{"legsSeparation":{"strength":-0.03679245283018867,"separationAngle":50},"stride":{"strength":0,"upperLegsPitch":30,"lowerLegsPitch":15,"upperLegsPitchOffset":0.2,"lowerLegsPitchOffset":1.5}},"joints":[{"name":"hips","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0,"thrust":0,"bob":0,"sway":0,"thrustPhase":180,"bobPhase":0,"swayPhase":-90,"thrustOffset":0,"bobOffset":0,"swayOffset":0},{"name":"upperLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"lowerLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"feet","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"toes","pitch":2.0377358490566038,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":4.415094339622641,"yawOffset":0,"rollOffset":0},{"name":"spine","pitch":1.660377358490566,"yaw":0,"roll":0,"pitchPhase":-180,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine1","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine2","pitch":2.1132075471698113,"yaw":0,"roll":0,"pitchPhase":-0.6792452830188722,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"shoulders","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0.6792452830188678,"yawOffset":-5.20754716981132,"rollOffset":-2.9433962264150937},{"name":"upperArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":49.584905660377345,"yawOffset":9.169811320754715,"rollOffset":0},{"name":"lowerArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"hands","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":-13,"yawOffset":-1.0188679245283017,"rollOffset":1.0188679245283017},{"name":"head","pitch":0,"yaw":1.7358490566037734,"roll":1.5094339622641508,"pitchPhase":-90.33962264150944,"yawPhase":94.41509433962267,"rollPhase":0,"pitchOffset":1.6981132075471694,"yawOffset":0,"rollOffset":0}]}; +var keyFrameThree = {"name":"FemaleStandingOne","settings":{"baseFrequency":70,"flyingHipsPitch":60,"takeFlightVelocity":40,"maxBankingAngle":40},"adjusters":{"legsSeparation":{"strength":-0.03679245283018867,"separationAngle":50},"stride":{"strength":0,"upperLegsPitch":30,"lowerLegsPitch":15,"upperLegsPitchOffset":0.2,"lowerLegsPitchOffset":1.5}},"joints":[{"name":"hips","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0,"thrust":0,"bob":0,"sway":0,"thrustPhase":180,"bobPhase":0,"swayPhase":-90,"thrustOffset":0,"bobOffset":0,"swayOffset":0},{"name":"upperLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"lowerLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"feet","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"toes","pitch":2.0377358490566038,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":4.415094339622641,"yawOffset":0,"rollOffset":0},{"name":"spine","pitch":1.660377358490566,"yaw":0,"roll":0,"pitchPhase":-180,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine1","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine2","pitch":2.1132075471698113,"yaw":0,"roll":0,"pitchPhase":-0.6792452830188722,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"shoulders","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":-21.0566037735849,"yawOffset":-5.20754716981132,"rollOffset":-2.9433962264150937},{"name":"upperArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":-33.28301886792452,"yawOffset":9.169811320754715,"rollOffset":0},{"name":"lowerArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"hands","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":-13,"yawOffset":-1.0188679245283017,"rollOffset":1.0188679245283017},{"name":"head","pitch":0,"yaw":1.7358490566037734,"roll":1.5094339622641508,"pitchPhase":-90.33962264150944,"yawPhase":94.41509433962267,"rollPhase":0,"pitchOffset":1.6981132075471694,"yawOffset":0,"rollOffset":0}]}; \ No newline at end of file From 32781d53a2cf2dd7efd5802fd6b48871f5caf1d4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 27 Oct 2014 17:35:37 -0700 Subject: [PATCH 02/50] Working on allowing different resolutions for heightfield heights and colors. --- interface/src/ui/MetavoxelEditor.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 8c5080c6f8..3568c15eb2 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1143,7 +1143,9 @@ void ImportHeightfieldTool::updatePreview() { float z = 0.0f; int blockSize = pow(2.0, _blockSize->value()); int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int colorSize = blockSize + HeightfieldBuffer::SHARED_EDGE; + int colorScale = glm::round(glm::log(_colorImage.height() / _heightImage.height()) / glm::log(2.0f)); + int colorBlockSize = blockSize * pow(2.0, colorScale); + int colorSize = colorBlockSize + HeightfieldBuffer::SHARED_EDGE; for (int i = 0; i < _heightImage.height(); i += blockSize, z++) { float x = 0.0f; for (int j = 0; j < _heightImage.width(); j += blockSize, x++) { @@ -1164,12 +1166,14 @@ void ImportHeightfieldTool::updatePreview() { } QByteArray color; if (!_colorImage.isNull()) { + int colorI = (i / blockSize) * colorBlockSize; + int colorJ = (j / blockSize) * colorBlockSize; color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0); - rows = qMax(0, qMin(colorSize, _colorImage.height() - i)); - columns = qMax(0, qMin(colorSize, _colorImage.width() - j)); + rows = qMax(0, qMin(colorSize, _colorImage.height() - colorI)); + columns = qMax(0, qMin(colorSize, _colorImage.width() - colorJ)); for (int y = 0; y < rows; y++) { memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES, - _colorImage.scanLine(i + y) + j * DataBlock::COLOR_BYTES, + _colorImage.scanLine(colorI + y) + colorJ * DataBlock::COLOR_BYTES, columns * DataBlock::COLOR_BYTES); } } From d1404aa7d3afcc4bbb89e23e2b2fab59de5c4b72 Mon Sep 17 00:00:00 2001 From: DaveDubUK Date: Tue, 28 Oct 2014 16:57:53 +0000 Subject: [PATCH 03/50] Minor code edits --- examples/followThroughOverlappingAction.js | 1080 +++++++++++++++----- 1 file changed, 834 insertions(+), 246 deletions(-) diff --git a/examples/followThroughOverlappingAction.js b/examples/followThroughOverlappingAction.js index 6ae910fa77..fdbd5e5550 100644 --- a/examples/followThroughOverlappingAction.js +++ b/examples/followThroughOverlappingAction.js @@ -1,37 +1,38 @@ - // +// // follow-through-and-overlapping-action.js // -// Simple demonstration showing the visual effect of adding the -// follow through and overlapping action technique to avatar movement. +// Simple demonstration showing the visual effect of adding the Disney +// follow through and overlapping action animation technique to avatar movement. // -// Designed and created by Ozan Serim and Davedub, August 2014 +// Designed and created by David Wooldridge and Ozan Serim, August 2014 // -// Version 1.0 +// Version 1.001 // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - -// always at the top... -var pathToOverlays = "https://s3-us-west-1.amazonaws.com/highfidelity-public/ArmSwingScript/ArmSwingOverlays/"; +// location of overlay images +var pathToOverlays = "https://s3.amazonaws.com/hifi-public/ArmSwingScript/ArmSwingOverlays/"; // animation var LEFT = 1; var RIGHT = 2; var DIRECTION = 0; +// min max +var weightMin = 1; +var weightMax = 20; +var jointEffectMax = 5; + // animation effect variables var handEffect = 3.4; // 0 to jointEffectMax var forearmEffect = 2.5; // 0 to jointEffectMax var limbWeight = 8; // will only use nearest integer, as defines an array length var effectStrength = 1; // 0 to 1 - overall effect strength -var overShoot = false; // false uses upper arm as driver for forearm and hand, true uses upper arm for forearm and lower arm as driver for hand. - -// min max -var weightMin = 1; -var weightMax = 20; -var jointEffectMax = 5; +// Overshoot: false uses upper arm as driver for forearm and hand +// true uses upper arm for forearm and lower arm as driver for hand. +var overShoot = false; // animate self (tap the 'r' key) var animateSelf = false; @@ -39,200 +40,213 @@ var selfAnimateFrequency = 7.5; // overlay values var handleValue = 0; -var controlPosX = Window.innerWidth/2 - 500; +var controlPosX = Window.innerWidth / 2 - 500; var controlPosY = 0; -var minSliderX = controlPosX+18; +var minSliderX = controlPosX + 18; var sliderRangeX = 190; -var minHandleX = controlPosX-50; -var handleRangeX = 350/2; +var minHandleX = controlPosX - 50; +var handleRangeX = 350 / 2; var handlePosition = 0; // background overlay var controllerBackground = Overlays.addOverlay("image", { - bounds: { x: controlPosX, y: controlPosY, width: 250, height: 380}, - imageURL: pathToOverlays+"flourish-augmentation-control-overlay.png", - color: { red: 255, green: 255, blue: 255}, + bounds: {x: controlPosX, y: controlPosY, width: 250, height: 380}, + imageURL: pathToOverlays + "flourish-augmentation-control-overlay.png", + color: {red: 255, green: 255, blue: 255}, alpha: 1 }); var controllerRadioSelectedBackground = Overlays.addOverlay("image", { - bounds: { x: controlPosX, y: controlPosY, width: 250, height: 380}, - imageURL: pathToOverlays+"flourish-augmentation-control-radio-selected-overlay.png", - color: { red: 255, green: 255, blue: 255}, + bounds: {x: controlPosX, y: controlPosY, width: 250, height: 380}, + imageURL: pathToOverlays + "flourish-augmentation-control-radio-selected-overlay.png", + color: {red: 255, green: 255, blue: 255}, alpha: 1, visible: false }); // handle overlay var applyMotionHandle = Overlays.addOverlay("image", { - bounds: { x: minHandleX+handleRangeX-39, y: controlPosY+232, width: 79, height: 100}, - imageURL: pathToOverlays+"flourish-augmentation-handle-overlay.png", - color: { red: 255, green: 255, blue: 255}, + bounds: {x: minHandleX+handleRangeX-39, y: controlPosY+232, + width: 79, height: 100}, + imageURL: pathToOverlays + "flourish-augmentation-handle-overlay.png", + color: {red: 255, green: 255, blue: 255}, alpha: 1 }); // slider overlays var handEffectSlider = Overlays.addOverlay("image", { - bounds: { x: minSliderX + (handEffect / jointEffectMax * sliderRangeX), y: controlPosY+46, width: 25, height: 25}, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, + bounds: {x: minSliderX + (handEffect / jointEffectMax * sliderRangeX), + y: controlPosY + 46, width: 25, height: 25}, + imageURL: pathToOverlays + "ddao-slider-handle.png", + color: {red: 255, green: 255, blue: 255}, alpha: 1 }); var forearmEffectSlider = Overlays.addOverlay("image", { - bounds: { x: minSliderX + (forearmEffect / jointEffectMax * sliderRangeX), y: controlPosY+86, width: 25, height: 25}, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, + bounds: {x: minSliderX + (forearmEffect / jointEffectMax * sliderRangeX), y: controlPosY + 86, + width: 25, height: 25}, + imageURL: pathToOverlays + "ddao-slider-handle.png", + color: {red: 255, green: 255, blue: 255}, alpha: 1 }); var limbWeightSlider = Overlays.addOverlay("image", { - bounds: { x: minSliderX + (limbWeight / weightMax * sliderRangeX), y: controlPosY+126, width: 25, height: 25}, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, + bounds: {x: minSliderX + (limbWeight / weightMax * sliderRangeX), y: controlPosY+126, + width: 25, height: 25}, + imageURL: pathToOverlays + "ddao-slider-handle.png", + color: {red: 255, green: 255, blue: 255}, alpha: 1 }); var effectStrengthSlider = Overlays.addOverlay("image", { - bounds: { x: minSliderX + (effectStrength * sliderRangeX), y: controlPosY+206, width: 25, height: 25}, - imageURL: pathToOverlays+"ddao-slider-handle.png", - color: { red: 255, green: 255, blue: 255}, + bounds: {x: minSliderX + (effectStrength * sliderRangeX), y: controlPosY+206, + width: 25, height: 25}, + imageURL: pathToOverlays + "ddao-slider-handle.png", + color: {red: 255, green: 255, blue: 255}, alpha: 1 }); // main loop - using averaging filters to add limb element follow-through var upperArmPDampingFilter = []; -upperArmPDampingFilter.length = parseInt(limbWeight); // sets amount of damping for upper arm pitch +upperArmPDampingFilter.length = parseInt(limbWeight); // sets amount of damping for upper arm pitch var forearmPDampingFilter = []; -forearmPDampingFilter.length = parseInt(limbWeight) + 2; // sets amount of damping for lower arm pitch +forearmPDampingFilter.length = parseInt(limbWeight) + 2; // sets amount of damping for lower arm pitch var cumulativeTime = 0; Script.update.connect(function(deltaTime) { - // used for self animating (press r to invoke) - cumulativeTime += deltaTime; + // used for self animating (press r to invoke) + cumulativeTime += deltaTime; - // blend three keyframes using handle position to determine progress between keyframes - var animationProgress = handleValue; + // blend three keyframes using handle position to determine progress between keyframes + var animationProgress = handleValue; - if(animateSelf) { - animationProgress = Math.sin(cumulativeTime * selfAnimateFrequency); - animationProgress++; - animationProgress/=2; - } + if(animateSelf) { + animationProgress = Math.sin(cumulativeTime * selfAnimateFrequency); + animationProgress++; + animationProgress /= 2; + } - var keyframeOneWeight = 0; - var keyframeTwoWeight = 0; - var keyframeThreeWeight = 0; + var keyframeOneWeight = 0; + var keyframeTwoWeight = 0; + var keyframeThreeWeight = 0; - if(movingHandle || animateSelf) { - keyframeOneWeight = 0; - keyframeTwoWeight = animationProgress; - keyframeThreeWeight = 1-animationProgress; - } - else if(!movingHandle) { - // idle - keyframeOneWeight = 1; - keyframeTwoWeight = 0; - keyframeThreeWeight = 0; - } + if(movingHandle || animateSelf) { + keyframeOneWeight = 0; + keyframeTwoWeight = animationProgress; + keyframeThreeWeight = 1 - animationProgress; + } + else if(!movingHandle) { + // idle + keyframeOneWeight = 1; + keyframeTwoWeight = 0; + keyframeThreeWeight = 0; + } - var shoulderPitch = - keyframeOneWeight * keyFrameOne.joints[8].pitchOffset + - keyframeTwoWeight * keyFrameTwo.joints[8].pitchOffset + - keyframeThreeWeight * keyFrameThree.joints[8].pitchOffset; + var shoulderPitch = + keyframeOneWeight * keyFrameOne.joints[8].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[8].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[8].pitchOffset; - var upperArmPitch = - keyframeOneWeight * keyFrameOne.joints[9].pitchOffset + - keyframeTwoWeight * keyFrameTwo.joints[9].pitchOffset + - keyframeThreeWeight * keyFrameThree.joints[9].pitchOffset; + var upperArmPitch = + keyframeOneWeight * keyFrameOne.joints[9].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[9].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[9].pitchOffset; - // get the change in upper arm pitch and use to add weight effect to forearm (always) and hand (only for overShoot 1) - var deltaUpperArmPitch = effectStrength * (upperArmPDampingFilter[upperArmPDampingFilter.length-1] - upperArmPDampingFilter[0]); + // get the change in upper arm pitch and use to add weight effect to forearm (always) and hand (only for overShoot) + var deltaUpperArmPitch = effectStrength * + (upperArmPDampingFilter[upperArmPDampingFilter.length - 1] - + upperArmPDampingFilter[0]); - var forearmPitch = - keyframeOneWeight * keyFrameOne.joints[10].pitchOffset + - keyframeTwoWeight * keyFrameTwo.joints[10].pitchOffset + - keyframeThreeWeight * keyFrameThree.joints[10].pitchOffset - - (deltaUpperArmPitch/(jointEffectMax - forearmEffect)); + var forearmPitch = + keyframeOneWeight * keyFrameOne.joints[10].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[10].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[10].pitchOffset - + (deltaUpperArmPitch/(jointEffectMax - forearmEffect)); - // there are two methods for calculating the hand follow through - var handPitch = 0; - if(overShoot) { - // get the change in forearm pitch and use to add weight effect to hand - var deltaForearmPitch = effectStrength * (forearmPDampingFilter[forearmPDampingFilter.length-1] - forearmPDampingFilter[0]); - handPitch = - keyframeOneWeight * keyFrameOne.joints[11].pitchOffset + - keyframeTwoWeight * keyFrameTwo.joints[11].pitchOffset + - keyframeThreeWeight * keyFrameThree.joints[11].pitchOffset + - (deltaForearmPitch/(jointEffectMax - handEffect)); // driven by forearm - } else { - handPitch = - keyframeOneWeight * keyFrameOne.joints[11].pitchOffset + - keyframeTwoWeight * keyFrameTwo.joints[11].pitchOffset + - keyframeThreeWeight * keyFrameThree.joints[11].pitchOffset - - (deltaUpperArmPitch/(jointEffectMax - handEffect)); // driven by upper arm - } + // there are two methods for calculating the hand follow through + var handPitch = 0; + if(overShoot) { - var shoulderYaw = - keyframeOneWeight * keyFrameOne.joints[8].yawOffset + - keyframeTwoWeight * keyFrameTwo.joints[8].yawOffset + - keyframeThreeWeight * keyFrameThree.joints[8].yawOffset; + // get the change in forearm pitch and use to add weight effect to hand + var deltaForearmPitch = effectStrength * + (forearmPDampingFilter[forearmPDampingFilter.length - 1] - + forearmPDampingFilter[0]); + handPitch = + keyframeOneWeight * keyFrameOne.joints[11].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[11].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[11].pitchOffset + + (deltaForearmPitch /(jointEffectMax - handEffect)); // hand driven by forearm - var upperArmYaw = - keyframeOneWeight * keyFrameOne.joints[9].yawOffset + - keyframeTwoWeight * keyFrameTwo.joints[9].yawOffset + - keyframeThreeWeight * keyFrameThree.joints[9].yawOffset; + } else { - var lowerArmYaw = - keyframeOneWeight * keyFrameOne.joints[10].yawOffset + - keyframeTwoWeight * keyFrameTwo.joints[10].yawOffset + - keyframeThreeWeight * keyFrameThree.joints[10].yawOffset; + handPitch = + keyframeOneWeight * keyFrameOne.joints[11].pitchOffset + + keyframeTwoWeight * keyFrameTwo.joints[11].pitchOffset + + keyframeThreeWeight * keyFrameThree.joints[11].pitchOffset - + (deltaUpperArmPitch /(jointEffectMax - handEffect)); // hand driven by upper arm + } - var handYaw = - keyframeOneWeight * keyFrameOne.joints[11].yawOffset + - keyframeTwoWeight * keyFrameTwo.joints[11].yawOffset + - keyframeThreeWeight * keyFrameThree.joints[11].yawOffset; + var shoulderYaw = + keyframeOneWeight * keyFrameOne.joints[8].yawOffset + + keyframeTwoWeight * keyFrameTwo.joints[8].yawOffset + + keyframeThreeWeight * keyFrameThree.joints[8].yawOffset; - var shoulderRoll = - keyframeOneWeight * keyFrameOne.joints[8].rollOffset + - keyframeTwoWeight * keyFrameTwo.joints[8].rollOffset + - keyframeThreeWeight * keyFrameThree.joints[8].rollOffset; + var upperArmYaw = + keyframeOneWeight * keyFrameOne.joints[9].yawOffset + + keyframeTwoWeight * keyFrameTwo.joints[9].yawOffset + + keyframeThreeWeight * keyFrameThree.joints[9].yawOffset; - var upperArmRoll = - keyframeOneWeight * keyFrameOne.joints[9].rollOffset + - keyframeTwoWeight * keyFrameTwo.joints[9].rollOffset + - keyframeThreeWeight * keyFrameThree.joints[9].rollOffset; + var lowerArmYaw = + keyframeOneWeight * keyFrameOne.joints[10].yawOffset + + keyframeTwoWeight * keyFrameTwo.joints[10].yawOffset + + keyframeThreeWeight * keyFrameThree.joints[10].yawOffset; - var lowerArmRoll = - keyframeOneWeight * keyFrameOne.joints[10].rollOffset + - keyframeTwoWeight * keyFrameTwo.joints[10].rollOffset + - keyframeThreeWeight * keyFrameThree.joints[10].rollOffset; + var handYaw = + keyframeOneWeight * keyFrameOne.joints[11].yawOffset + + keyframeTwoWeight * keyFrameTwo.joints[11].yawOffset + + keyframeThreeWeight * keyFrameThree.joints[11].yawOffset; - var handRoll = - keyframeOneWeight * keyFrameOne.joints[11].rollOffset + - keyframeTwoWeight * keyFrameTwo.joints[11].rollOffset + - keyframeThreeWeight * keyFrameThree.joints[11].rollOffset; + var shoulderRoll = + keyframeOneWeight * keyFrameOne.joints[8].rollOffset + + keyframeTwoWeight * keyFrameTwo.joints[8].rollOffset + + keyframeThreeWeight * keyFrameThree.joints[8].rollOffset; - // filter upper arm pitch - upperArmPDampingFilter.push(upperArmPitch); - upperArmPDampingFilter.shift(); - var upperArmPitchFiltered = 0; - for(ea in upperArmPDampingFilter) upperArmPitchFiltered += upperArmPDampingFilter[ea]; - upperArmPitchFiltered /= upperArmPDampingFilter.length; - upperArmPitch = (effectStrength*upperArmPitchFiltered) + ((1-(effectStrength)) * upperArmPitch); + var upperArmRoll = + keyframeOneWeight * keyFrameOne.joints[9].rollOffset + + keyframeTwoWeight * keyFrameTwo.joints[9].rollOffset + + keyframeThreeWeight * keyFrameThree.joints[9].rollOffset; - // filter forearm pitch only if using for hand follow-though - if(overShoot) { - forearmPDampingFilter.push(forearmPitch); - forearmPDampingFilter.shift(); - var forearmPitchFiltered = 0; - for(ea in forearmPDampingFilter) forearmPitchFiltered += forearmPDampingFilter[ea]; - forearmPitchFiltered /= forearmPDampingFilter.length; - forearmPitch = (effectStrength*forearmPitchFiltered) + ((1-(effectStrength)) * forearmPitch); - } + var lowerArmRoll = + keyframeOneWeight * keyFrameOne.joints[10].rollOffset + + keyframeTwoWeight * keyFrameTwo.joints[10].rollOffset + + keyframeThreeWeight * keyFrameThree.joints[10].rollOffset; - // apply the new rotation data to the joints - MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees( shoulderPitch, shoulderYaw, shoulderRoll)); - MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees( upperArmPitch, -upperArmYaw, upperArmRoll)); - MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees( forearmPitch, lowerArmYaw, lowerArmRoll)); - MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollDegrees( handPitch, handYaw, handRoll)); + var handRoll = + keyframeOneWeight * keyFrameOne.joints[11].rollOffset + + keyframeTwoWeight * keyFrameTwo.joints[11].rollOffset + + keyframeThreeWeight * keyFrameThree.joints[11].rollOffset; + + // filter upper arm pitch + upperArmPDampingFilter.push(upperArmPitch); + upperArmPDampingFilter.shift(); + var upperArmPitchFiltered = 0; + for(ea in upperArmPDampingFilter) upperArmPitchFiltered += upperArmPDampingFilter[ea]; + upperArmPitchFiltered /= upperArmPDampingFilter.length; + upperArmPitch = (effectStrength * upperArmPitchFiltered) + ((1 - effectStrength) * upperArmPitch); + + // filter forearm pitch only if using for hand follow-though + if(overShoot) { + + forearmPDampingFilter.push(forearmPitch); + forearmPDampingFilter.shift(); + var forearmPitchFiltered = 0; + for(ea in forearmPDampingFilter) forearmPitchFiltered += forearmPDampingFilter[ea]; + forearmPitchFiltered /= forearmPDampingFilter.length; + forearmPitch = (effectStrength*forearmPitchFiltered) + ((1-effectStrength) * forearmPitch); + } + + // apply the new rotation data to the joints + MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees(shoulderPitch, shoulderYaw, shoulderRoll)); + MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees(upperArmPitch, -upperArmYaw, upperArmRoll)); + MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees(forearmPitch, lowerArmYaw, lowerArmRoll)); + MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollDegrees(handPitch, handYaw, handRoll)); }); @@ -246,94 +260,102 @@ var movingHandle = false; function mousePressEvent(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - if(clickedOverlay===applyMotionHandle) movingHandle = true; - else if(clickedOverlay===handEffectSlider) movingHandEffectSlider = true; - else if(clickedOverlay===forearmEffectSlider) movingForearmEffectSlider = true; - else if(clickedOverlay===limbWeightSlider) movingLimbWeightSlider = true; - else if(clickedOverlay===effectStrengthSlider) movingEffectStrengthSlider = true; - else if(clickedOverlay===controllerRadioSelectedBackground && - event.x>477&&event.x<497&&event.y>338&&event.y<360) { - overShoot = false; - Overlays.editOverlay(controllerBackground, {visible:true}); - Overlays.editOverlay(controllerRadioSelectedBackground, {visible:false}); - } - else if(clickedOverlay===controllerBackground && - event.x>477&&event.x<497&&event.y>338&&event.y<360){ - overShoot = true; - Overlays.editOverlay(controllerBackground, {visible:false}); - Overlays.editOverlay(controllerRadioSelectedBackground, {visible:true}); - } + if(clickedOverlay === applyMotionHandle) movingHandle = true; + else if(clickedOverlay === handEffectSlider) movingHandEffectSlider = true; + else if(clickedOverlay === forearmEffectSlider) movingForearmEffectSlider = true; + else if(clickedOverlay === limbWeightSlider) movingLimbWeightSlider = true; + else if(clickedOverlay === effectStrengthSlider) movingEffectStrengthSlider = true; + else if(clickedOverlay === controllerRadioSelectedBackground && + event.x > 477 && event.x < 497 && event.y > 338 && event.y < 360) { + + overShoot = false; + Overlays.editOverlay(controllerBackground, {visible: true}); + Overlays.editOverlay(controllerRadioSelectedBackground, {visible: false}); + } + else if(clickedOverlay === controllerBackground && + event.x > 477 && event.x < 497 && event.y > 338 && event.y < 360){ + + overShoot = true; + Overlays.editOverlay(controllerBackground, {visible: false}); + Overlays.editOverlay(controllerRadioSelectedBackground, {visible: true}); + } } function mouseMoveEvent(event) { - if(movingHandle) { - var thumbClickOffsetX = event.x - minHandleX; - var thumbPositionNormalised = (thumbClickOffsetX - handleRangeX + 39) / handleRangeX; - if(thumbPositionNormalised<=-1) thumbPositionNormalised = -1; - else if(thumbPositionNormalised>1) thumbPositionNormalised = 1; + if(movingHandle) { - if(thumbPositionNormalised<0) DIRECTION = LEFT; - else DIRECTION = RIGHT; + var thumbClickOffsetX = event.x - minHandleX; + var thumbPositionNormalised = (thumbClickOffsetX - handleRangeX) / handleRangeX; + if(thumbPositionNormalised <= -1) thumbPositionNormalised = -1; + else if(thumbPositionNormalised > 1) thumbPositionNormalised = 1; - handleValue = (thumbPositionNormalised+1)/2; - var handleX = (thumbPositionNormalised * handleRangeX) + handleRangeX - 39; // sets range - Overlays.editOverlay(applyMotionHandle, { x: handleX + minHandleX} ); - return; - } - else if(movingHandEffectSlider) { - var thumbClickOffsetX = event.x - minSliderX; - var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; - if(thumbPositionNormalised<0) thumbPositionNormalised = 0; - if(thumbPositionNormalised>1) thumbPositionNormalised = 1; - handEffect = (thumbPositionNormalised-0.08) * jointEffectMax; - var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range - Overlays.editOverlay(handEffectSlider, { x: sliderX + minSliderX} ); - } - else if(movingForearmEffectSlider) { - var thumbClickOffsetX = event.x - minSliderX; - var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; - if(thumbPositionNormalised<0) thumbPositionNormalised = 0; - if(thumbPositionNormalised>1) thumbPositionNormalised = 1; - forearmEffect = (thumbPositionNormalised-0.1) * jointEffectMax; - var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range - Overlays.editOverlay(forearmEffectSlider, { x: sliderX + minSliderX} ); - } - else if(movingLimbWeightSlider) { - var thumbClickOffsetX = event.x - minSliderX; - var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; - if(thumbPositionNormalised<0) thumbPositionNormalised = 0; - if(thumbPositionNormalised>1) thumbPositionNormalised = 1; - limbWeight = thumbPositionNormalised * weightMax; - if(limbWeight1) thumbPositionNormalised = 1; - effectStrength = thumbPositionNormalised; - var sliderX = thumbPositionNormalised * sliderRangeX ; // sets range - Overlays.editOverlay(effectStrengthSlider, { x: sliderX + minSliderX} ); - return; - } + if(thumbPositionNormalised < 0) DIRECTION = LEFT; + else DIRECTION = RIGHT; + + handleValue = (thumbPositionNormalised + 1) / 2; + var handleX = (thumbPositionNormalised * handleRangeX) + handleRangeX - 39; + Overlays.editOverlay(applyMotionHandle, {x: handleX + minHandleX}); + return; + } + else if(movingHandEffectSlider) { + + var thumbClickOffsetX = event.x - minSliderX; + var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; + if(thumbPositionNormalised < 0) thumbPositionNormalised = 0; + if(thumbPositionNormalised > 1) thumbPositionNormalised = 1; + handEffect = (thumbPositionNormalised - 0.08) * jointEffectMax; + var sliderX = thumbPositionNormalised * sliderRangeX ; + Overlays.editOverlay(handEffectSlider, {x: sliderX + minSliderX}); + } + else if(movingForearmEffectSlider) { + + var thumbClickOffsetX = event.x - minSliderX; + var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; + if(thumbPositionNormalised < 0) thumbPositionNormalised = 0; + if(thumbPositionNormalised > 1) thumbPositionNormalised = 1; + forearmEffect = (thumbPositionNormalised - 0.1) * jointEffectMax; + var sliderX = thumbPositionNormalised * sliderRangeX ; + Overlays.editOverlay(forearmEffectSlider, {x: sliderX + minSliderX}); + } + else if(movingLimbWeightSlider) { + + var thumbClickOffsetX = event.x - minSliderX; + var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; + if(thumbPositionNormalised<0) thumbPositionNormalised = 0; + if(thumbPositionNormalised>1) thumbPositionNormalised = 1; + limbWeight = thumbPositionNormalised * weightMax; + if(limbWeight < weightMin) limbWeight = weightMin; + upperArmPDampingFilter.length = parseInt(limbWeight); + var sliderX = thumbPositionNormalised * sliderRangeX ; + Overlays.editOverlay(limbWeightSlider, {x: sliderX + minSliderX}); + } + else if(movingEffectStrengthSlider) { + + var thumbClickOffsetX = event.x - minSliderX; + var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX; + if(thumbPositionNormalised < 0) thumbPositionNormalised = 0; + if(thumbPositionNormalised > 1) thumbPositionNormalised = 1; + effectStrength = thumbPositionNormalised; + var sliderX = thumbPositionNormalised * sliderRangeX ; + Overlays.editOverlay(effectStrengthSlider, {x: sliderX + minSliderX}); + return; + } } function mouseReleaseEvent(event) { - if(movingHandle) { - movingHandle = false; - handleValue = 0; - Overlays.editOverlay(applyMotionHandle, { x: minHandleX+handleRangeX-39} ); - } - else if(movingHandEffectSlider) movingHandEffectSlider = false; - else if(movingForearmEffectSlider) movingForearmEffectSlider = false; - else if(movingLimbWeightSlider) movingLimbWeightSlider = false; - else if(movingEffectStrengthSlider) movingEffectStrengthSlider = false; - else if(movingDampingSlider) movingDampingSlider = false; + if(movingHandle) { + + movingHandle = false; + handleValue = 0; + Overlays.editOverlay(applyMotionHandle, {x: minHandleX+handleRangeX - 39}); + } + else if(movingHandEffectSlider) movingHandEffectSlider = false; + else if(movingForearmEffectSlider) movingForearmEffectSlider = false; + else if(movingLimbWeightSlider) movingLimbWeightSlider = false; + else if(movingEffectStrengthSlider) movingEffectStrengthSlider = false; + else if(movingDampingSlider) movingDampingSlider = false; } // set up mouse and keyboard callbacks @@ -345,23 +367,23 @@ Controller.keyPressEvent.connect(keyPressEvent); // keyboard command function keyPressEvent(event) { - if (event.text == "q") { - print('hand effect = ' + handEffect + '\n'); + if (event.text == "q") { + print('hand effect = ' + handEffect + '\n'); print('forearmEffect = ' + forearmEffect + '\n'); print('limbWeight = ' + limbWeight + '\n'); print('effectStrength = ' + effectStrength + '\n'); } else if (event.text == "r") { - animateSelf = !animateSelf; + animateSelf = !animateSelf; } else if (event.text == "[") { - selfAnimateFrequency+=0.5 - print('selfAnimateFrequency = '+selfAnimateFrequency); - } - else if (event.text == "]") { - selfAnimateFrequency-=0.5 - print('selfAnimateFrequency = '+selfAnimateFrequency); - } + selfAnimateFrequency += 0.5; + print('selfAnimateFrequency = '+selfAnimateFrequency); + } + else if (event.text == "]") { + selfAnimateFrequency -= 0.5; + print('selfAnimateFrequency = '+selfAnimateFrequency); + } } @@ -369,15 +391,14 @@ function keyPressEvent(event) { function resetJoints() { var avatarJointNames = MyAvatar.getJointNames(); - for (var i = 0; i < avatarJointNames.length; i++) { + for (var i = 0; i < avatarJointNames.length; i++) MyAvatar.clearJointData(avatarJointNames[i]); - } } // Script ending Script.scriptEnding.connect(function() { - // delete the overlays + // delete the overlays Overlays.deleteOverlay(controllerBackground); Overlays.deleteOverlay(controllerRadioSelectedBackground); Overlays.deleteOverlay(handEffectSlider); @@ -391,8 +412,575 @@ Script.scriptEnding.connect(function() { }); -// animation / joint data. animation keyframes produced using davedub's procedural animation controller -MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(80,0,0)); // stand nicely please -var keyFrameOne = {"name":"FemaleStandingOne","settings":{"baseFrequency":70,"flyingHipsPitch":60,"takeFlightVelocity":40,"maxBankingAngle":40},"adjusters":{"legsSeparation":{"strength":-0.03679245283018867,"separationAngle":50},"stride":{"strength":0,"upperLegsPitch":30,"lowerLegsPitch":15,"upperLegsPitchOffset":0.2,"lowerLegsPitchOffset":1.5}},"joints":[{"name":"hips","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0,"thrust":0,"bob":0,"sway":0,"thrustPhase":180,"bobPhase":0,"swayPhase":-90,"thrustOffset":0,"bobOffset":0,"swayOffset":0},{"name":"upperLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"lowerLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"feet","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"toes","pitch":2.0377358490566038,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":4.415094339622641,"yawOffset":0,"rollOffset":0},{"name":"spine","pitch":1.660377358490566,"yaw":0,"roll":0,"pitchPhase":-180,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine1","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine2","pitch":2.1132075471698113,"yaw":0,"roll":0,"pitchPhase":-0.6792452830188722,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"shoulders","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0.6792452830188678,"yawOffset":-5.20754716981132,"rollOffset":-2.9433962264150937},{"name":"upperArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":77.77358490566039,"yawOffset":9.169811320754715,"rollOffset":0},{"name":"lowerArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"hands","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":1.6981132075471694,"yawOffset":-1.0188679245283017,"rollOffset":1.0188679245283017},{"name":"head","pitch":0,"yaw":1.7358490566037734,"roll":1.5094339622641508,"pitchPhase":-90.33962264150944,"yawPhase":94.41509433962267,"rollPhase":0,"pitchOffset":1.6981132075471694,"yawOffset":0,"rollOffset":0}]}; -var keyFrameTwo = {"name":"FemaleStandingOne","settings":{"baseFrequency":70,"flyingHipsPitch":60,"takeFlightVelocity":40,"maxBankingAngle":40},"adjusters":{"legsSeparation":{"strength":-0.03679245283018867,"separationAngle":50},"stride":{"strength":0,"upperLegsPitch":30,"lowerLegsPitch":15,"upperLegsPitchOffset":0.2,"lowerLegsPitchOffset":1.5}},"joints":[{"name":"hips","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0,"thrust":0,"bob":0,"sway":0,"thrustPhase":180,"bobPhase":0,"swayPhase":-90,"thrustOffset":0,"bobOffset":0,"swayOffset":0},{"name":"upperLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"lowerLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"feet","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"toes","pitch":2.0377358490566038,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":4.415094339622641,"yawOffset":0,"rollOffset":0},{"name":"spine","pitch":1.660377358490566,"yaw":0,"roll":0,"pitchPhase":-180,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine1","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine2","pitch":2.1132075471698113,"yaw":0,"roll":0,"pitchPhase":-0.6792452830188722,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"shoulders","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0.6792452830188678,"yawOffset":-5.20754716981132,"rollOffset":-2.9433962264150937},{"name":"upperArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":49.584905660377345,"yawOffset":9.169811320754715,"rollOffset":0},{"name":"lowerArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"hands","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":-13,"yawOffset":-1.0188679245283017,"rollOffset":1.0188679245283017},{"name":"head","pitch":0,"yaw":1.7358490566037734,"roll":1.5094339622641508,"pitchPhase":-90.33962264150944,"yawPhase":94.41509433962267,"rollPhase":0,"pitchOffset":1.6981132075471694,"yawOffset":0,"rollOffset":0}]}; -var keyFrameThree = {"name":"FemaleStandingOne","settings":{"baseFrequency":70,"flyingHipsPitch":60,"takeFlightVelocity":40,"maxBankingAngle":40},"adjusters":{"legsSeparation":{"strength":-0.03679245283018867,"separationAngle":50},"stride":{"strength":0,"upperLegsPitch":30,"lowerLegsPitch":15,"upperLegsPitchOffset":0.2,"lowerLegsPitchOffset":1.5}},"joints":[{"name":"hips","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0,"thrust":0,"bob":0,"sway":0,"thrustPhase":180,"bobPhase":0,"swayPhase":-90,"thrustOffset":0,"bobOffset":0,"swayOffset":0},{"name":"upperLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"lowerLegs","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"feet","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"toes","pitch":2.0377358490566038,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":4.415094339622641,"yawOffset":0,"rollOffset":0},{"name":"spine","pitch":1.660377358490566,"yaw":0,"roll":0,"pitchPhase":-180,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine1","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"spine2","pitch":2.1132075471698113,"yaw":0,"roll":0,"pitchPhase":-0.6792452830188722,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"shoulders","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":-21.0566037735849,"yawOffset":-5.20754716981132,"rollOffset":-2.9433962264150937},{"name":"upperArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":-33.28301886792452,"yawOffset":9.169811320754715,"rollOffset":0},{"name":"lowerArms","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":0,"yawOffset":0,"rollOffset":0},{"name":"hands","pitch":0,"yaw":0,"roll":0,"pitchPhase":0,"yawPhase":0,"rollPhase":0,"pitchOffset":-13,"yawOffset":-1.0188679245283017,"rollOffset":1.0188679245283017},{"name":"head","pitch":0,"yaw":1.7358490566037734,"roll":1.5094339622641508,"pitchPhase":-90.33962264150944,"yawPhase":94.41509433962267,"rollPhase":0,"pitchOffset":1.6981132075471694,"yawOffset":0,"rollOffset":0}]}; \ No newline at end of file +// animation data. animation keyframes produced using walk.js +MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(80,0,0)); +var keyFrameOne = +{ + "name":"FemaleStandingOne", + "settings":{ + "baseFrequency":70, + "flyingHipsPitch":60, + "takeFlightVelocity":40, + "maxBankingAngle":40 + }, + "adjusters":{ + "legsSeparation":{ + "strength":-0.03679245283018867, + "separationAngle":50 + }, + "stride":{ + "strength":0, + "upperLegsPitch":30, + "lowerLegsPitch":15, + "upperLegsPitchOffset":0.2, + "lowerLegsPitchOffset":1.5 + } + }, + "joints":[ + { + "name":"hips", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0, + "thrust":0, + "bob":0, + "sway":0, + "thrustPhase":180, + "bobPhase":0, + "swayPhase":-90, + "thrustOffset":0, + "bobOffset":0, + "swayOffset":0 + }, + { + "name":"upperLegs", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"lowerLegs", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"feet", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"toes", + "pitch":2.0377358490566038, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":4.415094339622641, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"spine", + "pitch":1.660377358490566, + "yaw":0, + "roll":0, + "pitchPhase":-180, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"spine1", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"spine2", + "pitch":2.1132075471698113, + "yaw":0, + "roll":0, + "pitchPhase":-0.6792452830188722, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"shoulders", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0.6792452830188678, + "yawOffset":-5.20754716981132, + "rollOffset":-2.9433962264150937 + }, + { + "name":"upperArms", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":77.77358490566039, + "yawOffset":9.169811320754715, + "rollOffset":0 + }, + { + "name":"lowerArms", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"hands", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":1.6981132075471694, + "yawOffset":-1.0188679245283017, + "rollOffset":1.0188679245283017 + }, + { + "name":"head", + "pitch":0, + "yaw":1.7358490566037734, + "roll":1.5094339622641508, + "pitchPhase":-90.33962264150944, + "yawPhase":94.41509433962267, + "rollPhase":0, + "pitchOffset":1.6981132075471694, + "yawOffset":0, + "rollOffset":0 + } + ] +}; +var keyFrameTwo = +{ + "name":"FemaleStandingOne", + "settings":{ + "baseFrequency":70, + "flyingHipsPitch":60, + "takeFlightVelocity":40, + "maxBankingAngle":40 + }, + "adjusters":{ + "legsSeparation":{ + "strength":-0.03679245283018867, + "separationAngle":50 + }, + "stride":{ + "strength":0, + "upperLegsPitch":30, + "lowerLegsPitch":15, + "upperLegsPitchOffset":0.2, + "lowerLegsPitchOffset":1.5 + } + }, + "joints":[ + { + "name":"hips", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0, + "thrust":0, + "bob":0, + "sway":0, + "thrustPhase":180, + "bobPhase":0, + "swayPhase":-90, + "thrustOffset":0, + "bobOffset":0, + "swayOffset":0 + }, + { + "name":"upperLegs", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"lowerLegs", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"feet", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"toes", + "pitch":2.0377358490566038, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":4.415094339622641, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"spine", + "pitch":1.660377358490566, + "yaw":0, + "roll":0, + "pitchPhase":-180, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"spine1", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"spine2", + "pitch":2.1132075471698113, + "yaw":0, + "roll":0, + "pitchPhase":-0.6792452830188722, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"shoulders", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0.6792452830188678, + "yawOffset":-5.20754716981132, + "rollOffset":-2.9433962264150937 + }, + { + "name":"upperArms", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":49.584905660377345, + "yawOffset":9.169811320754715, + "rollOffset":0 + }, + { + "name":"lowerArms", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"hands", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":-13, + "yawOffset":-1.0188679245283017, + "rollOffset":1.0188679245283017 + }, + { + "name":"head", + "pitch":0, + "yaw":1.7358490566037734, + "roll":1.5094339622641508, + "pitchPhase":-90.33962264150944, + "yawPhase":94.41509433962267, + "rollPhase":0, + "pitchOffset":1.6981132075471694, + "yawOffset":0, + "rollOffset":0 + } + ] +}; +var keyFrameThree = +{ + "name":"FemaleStandingOne", + "settings":{ + "baseFrequency":70, + "flyingHipsPitch":60, + "takeFlightVelocity":40, + "maxBankingAngle":40 + }, + "adjusters":{ + "legsSeparation":{ + "strength":-0.03679245283018867, + "separationAngle":50 + }, + "stride":{ + "strength":0, + "upperLegsPitch":30, + "lowerLegsPitch":15, + "upperLegsPitchOffset":0.2, + "lowerLegsPitchOffset":1.5 + } + }, + "joints":[ + { + "name":"hips", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0, + "thrust":0, + "bob":0, + "sway":0, + "thrustPhase":180, + "bobPhase":0, + "swayPhase":-90, + "thrustOffset":0, + "bobOffset":0, + "swayOffset":0 + }, + { + "name":"upperLegs", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"lowerLegs", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"feet", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"toes", + "pitch":2.0377358490566038, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":4.415094339622641, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"spine", + "pitch":1.660377358490566, + "yaw":0, + "roll":0, + "pitchPhase":-180, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"spine1", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"spine2", + "pitch":2.1132075471698113, + "yaw":0, + "roll":0, + "pitchPhase":-0.6792452830188722, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"shoulders", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":-21.0566037735849, + "yawOffset":-5.20754716981132, + "rollOffset":-2.9433962264150937 + }, + { + "name":"upperArms", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":-33.28301886792452, + "yawOffset":9.169811320754715, + "rollOffset":0 + }, + { + "name":"lowerArms", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":0, + "yawOffset":0, + "rollOffset":0 + }, + { + "name":"hands", + "pitch":0, + "yaw":0, + "roll":0, + "pitchPhase":0, + "yawPhase":0, + "rollPhase":0, + "pitchOffset":-13, + "yawOffset":-1.0188679245283017, + "rollOffset":1.0188679245283017 + }, + { + "name":"head", + "pitch":0, + "yaw":1.7358490566037734, + "roll":1.5094339622641508, + "pitchPhase":-90.33962264150944, + "yawPhase":94.41509433962267, + "rollPhase":0, + "pitchOffset":1.6981132075471694, + "yawOffset":0, + "rollOffset":0 + } + ] +}; \ No newline at end of file From d28da9250f218bcaf9dd6ce5d74d0cfa6922f81e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 28 Oct 2014 18:31:34 -0700 Subject: [PATCH 04/50] More progress towards allowing different resolutions for height and color, material, etc. --- interface/src/MetavoxelSystem.cpp | 357 +++++++++++------- interface/src/MetavoxelSystem.h | 2 +- interface/src/ui/MetavoxelEditor.cpp | 54 ++- .../metavoxels/src/AttributeRegistry.cpp | 12 + libraries/metavoxels/src/AttributeRegistry.h | 6 + 5 files changed, 281 insertions(+), 150 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 20e05e779e..b9baa356f5 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1006,16 +1006,14 @@ QByteArray HeightfieldBuffer::getUnextendedHeight() const { return unextended; } -QByteArray HeightfieldBuffer::getUnextendedColor() const { - int srcSize = glm::sqrt(float(_color.size() / DataBlock::COLOR_BYTES)); - int destSize = srcSize - 1; - QByteArray unextended(destSize * destSize * DataBlock::COLOR_BYTES, 0); - const char* src = _color.constData(); - int srcStride = srcSize * DataBlock::COLOR_BYTES; +QByteArray HeightfieldBuffer::getUnextendedColor(int x, int y) const { + int unextendedSize = _heightSize - HEIGHT_EXTENSION; + QByteArray unextended(unextendedSize * unextendedSize * DataBlock::COLOR_BYTES, 0); char* dest = unextended.data(); - int destStride = destSize * DataBlock::COLOR_BYTES; - for (int z = 0; z < destSize; z++, src += srcStride, dest += destStride) { - memcpy(dest, src, destStride); + const char* src = _color.constData() + (y * _colorSize + x) * unextendedSize * DataBlock::COLOR_BYTES; + for (int z = 0; z < unextendedSize; z++, dest += unextendedSize * DataBlock::COLOR_BYTES, + src += _colorSize * DataBlock::COLOR_BYTES) { + memcpy(dest, src, unextendedSize * DataBlock::COLOR_BYTES); } return unextended; } @@ -1702,134 +1700,170 @@ public: HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections); - void init(HeightfieldBuffer* buffer) { _buffer = buffer; } + void init(HeightfieldBuffer* buffer); virtual int visit(MetavoxelInfo& info); + virtual bool postVisit(MetavoxelInfo& info); private: const QVector& _intersections; HeightfieldBuffer* _buffer; + + int _heightDepth; + int _colorDepth; + int _materialDepth; }; HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << - AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector(), lod), + AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << + AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector(), lod), _intersections(intersections) { } +void HeightfieldFetchVisitor::init(HeightfieldBuffer* buffer) { + _buffer = buffer; + _heightDepth = -1; + _colorDepth = -1; + _materialDepth = -1; +} + int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { - Box bounds = info.getBounds(); - const Box& heightBounds = _buffer->getHeightBounds(); - if (!bounds.intersects(heightBounds)) { + if (!info.getBounds().intersects(_buffer->getHeightBounds())) { return STOP_RECURSION; } - if (!info.isLeaf && info.size > _buffer->getScale()) { + if (!info.isLeaf) { return DEFAULT_ORDER; } + postVisit(info); + return STOP_RECURSION; +} + +bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - if (!height) { - return STOP_RECURSION; + if (height || _heightDepth != -1) { + if (_depth < _heightDepth) { + height.reset(); + } + _heightDepth = _depth; } + HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); + if (color || _colorDepth != -1) { + if (_depth < _colorDepth) { + color.reset(); + } + _colorDepth = _depth; + } + HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); + if (material || _materialDepth != -1) { + if (_depth < _materialDepth) { + material.reset(); + } + _materialDepth = _depth; + } + if (!(height || color || material)) { + return false; + } + Box bounds = info.getBounds(); foreach (const Box& intersection, _intersections) { Box overlap = intersection.getIntersection(bounds); if (overlap.isEmpty()) { continue; } - float heightIncrement = _buffer->getHeightIncrement(); - int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement; - int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement; - int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement); - int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement); - int heightSize = _buffer->getHeightSize(); - char* dest = _buffer->getHeight().data() + destY * heightSize + destX; - - const QByteArray& srcHeight = height->getContents(); - int srcSize = glm::sqrt(float(srcHeight.size())); - float srcIncrement = info.size / srcSize; - - if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + if (height) { + float heightIncrement = _buffer->getHeightIncrement(); + const Box& heightBounds = _buffer->getHeightBounds(); + int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement; + int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement; + int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement); + int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement); + int heightSize = _buffer->getHeightSize(); + char* dest = _buffer->getHeight().data() + destY * heightSize + destX; - const char* src = srcHeight.constData() + srcY * srcSize + srcX; - for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) { - memcpy(dest, src, destWidth); - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = heightIncrement / srcIncrement; - int shift = 0; - float size = _buffer->getScale(); - while (size < info.size) { - shift++; - size *= 2.0f; - } - int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale(); - for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) { - const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { - *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM); + const QByteArray& srcHeight = height->getContents(); + int srcSize = glm::sqrt((float)srcHeight.size()); + float srcIncrement = info.size / srcSize; + + if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) { + // easy case: same resolution + int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + + const char* src = srcHeight.constData() + srcY * srcSize + srcX; + for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) { + memcpy(dest, src, destWidth); + } + } else { + // more difficult: different resolutions + float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + float srcAdvance = heightIncrement / srcIncrement; + int shift = 0; + float size = _buffer->getScale(); + while (size < info.size) { + shift++; + size *= 2.0f; + } + int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale(); + for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) { + const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { + *lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM); + } } } } - - int colorSize = _buffer->getColorSize(); - if (colorSize == 0) { - continue; - } - HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - if (!color) { - continue; - } - const Box& colorBounds = _buffer->getColorBounds(); - overlap = colorBounds.getIntersection(overlap); - float colorIncrement = _buffer->getColorIncrement(); - destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement; - destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement; - destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement); - destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement); - dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES; - int destStride = colorSize * DataBlock::COLOR_BYTES; - int destBytes = destWidth * DataBlock::COLOR_BYTES; - - const QByteArray& srcColor = color->getContents(); - srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES)); - int srcStride = srcSize * DataBlock::COLOR_BYTES; - srcIncrement = info.size / srcSize; - - if (srcIncrement == colorIncrement) { - // easy case: same resolution - int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + if (color) { + const Box& colorBounds = _buffer->getColorBounds(); + overlap = colorBounds.getIntersection(overlap); + float colorIncrement = _buffer->getColorIncrement(); + int destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement; + int destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement; + int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement); + int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement); + int colorSize = _buffer->getColorSize(); + char* dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES; + int destStride = colorSize * DataBlock::COLOR_BYTES; + int destBytes = destWidth * DataBlock::COLOR_BYTES; - const char* src = srcColor.constData() + (srcY * srcSize + srcX) * DataBlock::COLOR_BYTES; - for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) { - memcpy(dest, src, destBytes); - } - } else { - // more difficult: different resolutions - float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; - float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; - float srcAdvance = colorIncrement / srcIncrement; - for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) { - const char* src = srcColor.constData() + (int)srcY * srcStride; - float lineSrcX = srcX; - for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += DataBlock::COLOR_BYTES, - lineSrcX += srcAdvance) { - const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES; - lineDest[0] = lineSrc[0]; - lineDest[1] = lineSrc[1]; - lineDest[2] = lineSrc[2]; + const QByteArray& srcColor = color->getContents(); + int srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES)); + int srcStride = srcSize * DataBlock::COLOR_BYTES; + float srcIncrement = info.size / srcSize; + + if (srcIncrement == colorIncrement) { + // easy case: same resolution + int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + + const char* src = srcColor.constData() + (srcY * srcSize + srcX) * DataBlock::COLOR_BYTES; + for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) { + memcpy(dest, src, destBytes); + } + } else { + // more difficult: different resolutions + float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + float srcAdvance = colorIncrement / srcIncrement; + for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) { + const char* src = srcColor.constData() + (int)srcY * srcStride; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += DataBlock::COLOR_BYTES, + lineSrcX += srcAdvance) { + const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES; + lineDest[0] = lineSrc[0]; + lineDest[1] = lineSrc[1]; + lineDest[2] = lineSrc[2]; + } } } } + if (material) { + } } - return STOP_RECURSION; + return false; } class HeightfieldRegionVisitor : public MetavoxelVisitor { @@ -1841,11 +1875,27 @@ public: HeightfieldRegionVisitor(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); + virtual bool postVisit(MetavoxelInfo& info); private: void addRegion(const Box& unextended, const Box& extended); + int _heightSize; + int _heightDepth; + + int _inheritedColorSize; + int _inheritedColorDepth; + + int _containedColorSize; + int _containedColorDepth; + + int _inheritedMaterialSize; + int _inheritedMaterialDepth; + + int _containedMaterialSize; + int _containedMaterialDepth; + QVector _intersections; HeightfieldFetchVisitor _fetchVisitor; }; @@ -1857,52 +1907,92 @@ HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), regionBounds(glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX), glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX)), + _heightDepth(-1), + _inheritedColorDepth(-1), + _containedColorDepth(-1), + _inheritedMaterialDepth(-1), + _containedMaterialDepth(-1), _fetchVisitor(lod, _intersections) { } +static int signedLeftShift(int value, int shift) { + return (shift > 0) ? value << shift : value >> (-shift); +} + int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { - if (!info.isLeaf) { - return DEFAULT_ORDER; - } - HeightfieldBuffer* buffer = NULL; HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); + int order = DEFAULT_ORDER; if (height) { - const QByteArray& heightContents = height->getContents(); - int size = glm::sqrt(float(heightContents.size())); - int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION; - int heightContentsSize = extendedSize * extendedSize; - - HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - int colorContentsSize = 0; - if (color) { - const QByteArray& colorContents = color->getContents(); - int colorSize = glm::sqrt(float(colorContents.size() / DataBlock::COLOR_BYTES)); - int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE; - colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES; + _heightSize = glm::sqrt((float)height->getContents().size()); + _heightDepth = _depth; + order |= ALL_NODES_REST; + } + HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); + if (color) { + int colorSize = glm::sqrt((float)(color->getContents().size() / DataBlock::COLOR_BYTES)); + if (_heightDepth == -1) { + _inheritedColorSize = colorSize; + _inheritedColorDepth = _depth; + + } else if (_containedColorDepth == -1 || + colorSize > signedLeftShift(_containedColorSize, _containedColorDepth - _depth)) { + _containedColorSize = colorSize; + _containedColorDepth = _depth; } + } + HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); + if (material) { + int materialSize = glm::sqrt((float)material->getContents().size()); + if (_heightDepth == -1) { + _inheritedMaterialSize = materialSize; + _inheritedMaterialDepth = _depth; - HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); - QByteArray materialContents; - QVector materials; - if (material) { - materialContents = material->getContents(); - materials = material->getMaterials(); + } else if (_containedMaterialDepth == -1 || + materialSize > signedLeftShift(_containedMaterialSize, _containedMaterialDepth - _depth)) { + _containedMaterialSize = materialSize; + _containedMaterialDepth = _depth; } + } + if (!info.isLeaf) { + return order; + } + postVisit(info); + return STOP_RECURSION; +} + +bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { + HeightfieldBuffer* buffer = NULL; + if (_depth == _heightDepth) { + int extendedHeightSize = _heightSize + HeightfieldBuffer::HEIGHT_EXTENSION; + int heightContentsSize = extendedHeightSize * extendedHeightSize; + + int extendedColorSize = qMax(_inheritedColorDepth == -1 ? 0 : + signedLeftShift(_inheritedColorSize, _inheritedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, + _containedColorDepth == -1 ? 0 : + signedLeftShift(_containedColorSize, _containedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); + int colorContentsSize = extendedColorSize * extendedColorSize; + + int extendedMaterialSize = qMax(_inheritedMaterialDepth == -1 ? 0 : + signedLeftShift(_inheritedMaterialSize, _inheritedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, + _containedMaterialDepth == -1 ? 0 : + signedLeftShift(_containedMaterialSize, _containedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); + int materialContentsSize = extendedMaterialSize * extendedMaterialSize; const HeightfieldBuffer* existingBuffer = static_cast( info.inputValues.at(3).getInlineValue().data()); Box bounds = info.getBounds(); if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && - existingBuffer->getColor().size() == colorContentsSize) { - // we already have a buffer of the correct resolution + existingBuffer->getColor().size() == colorContentsSize && + existingBuffer->getMaterial().size() == materialContentsSize) { + // we already have a buffer of the correct resolution addRegion(bounds, existingBuffer->getHeightBounds()); buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), - existingBuffer->getColor(), materialContents, materials); - + existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials()); + } else { // we must create a new buffer and update its borders buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), - QByteArray(colorContentsSize, 0), materialContents, materials); + QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0)); const Box& heightBounds = buffer->getHeightBounds(); addRegion(bounds, heightBounds); @@ -1919,10 +2009,17 @@ int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { _fetchVisitor.init(buffer); _data->guide(_fetchVisitor); } + _heightDepth = _containedColorDepth = _containedMaterialDepth = -1; + } + if (_depth == _inheritedColorDepth) { + _inheritedColorDepth = -1; + } + if (_depth == _inheritedMaterialDepth) { + _inheritedMaterialDepth = -1; } BufferDataPointer pointer(buffer); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); - return STOP_RECURSION; + return true; } void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 08cc1098ca..0dad7ee179 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -249,7 +249,7 @@ public: const QVector& getMaterials() const { return _materials; } QByteArray getUnextendedHeight() const; - QByteArray getUnextendedColor() const; + QByteArray getUnextendedColor(int x = 0, int y = 0) const; int getHeightSize() const { return _heightSize; } float getHeightIncrement() const { return _heightIncrement; } diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 3568c15eb2..e5a1f7f7a4 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1038,26 +1038,42 @@ void ImportHeightfieldTool::apply() { data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue( AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer)))); - QByteArray color; - if (buffer->getColor().isEmpty()) { - const unsigned char WHITE_VALUE = 0xFF; - color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE); - } else { - color = buffer->getUnextendedColor(); - } - HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue( - AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer)))); - - int size = glm::sqrt(float(height.size())); - QByteArray material(size * size, 0); - HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material)); - data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(AttributeValue( - AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), encodeInline(materialPointer)))); - MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit( _translation->getValue() + buffer->getTranslation() * scale, data)) }; Application::getInstance()->getMetavoxels()->applyEdit(message, true); + + int colorUnits = buffer->getColor().isEmpty() ? 1 : (buffer->getColorSize() - HeightfieldBuffer::SHARED_EDGE) / + (buffer->getHeightSize() - HeightfieldBuffer::HEIGHT_EXTENSION); + float colorScale = scale / colorUnits; + + for (int y = 0; y < colorUnits; y++) { + for (int x = 0; x < colorUnits; x++) { + MetavoxelData data; + data.setSize(colorScale); + + QByteArray color; + if (buffer->getColor().isEmpty()) { + const unsigned char WHITE_VALUE = 0xFF; + color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE); + } else { + color = buffer->getUnextendedColor(x, y); + } + HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color)); + data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode( + AttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), + encodeInline(colorPointer)))); + + QByteArray material(height.size(), 0); + HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material)); + data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode( + AttributeValue(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), + encodeInline(materialPointer)))); + + MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit( + _translation->getValue() + buffer->getTranslation() * scale + glm::vec3(x, 0.0f, y) * colorScale, data)) }; + Application::getInstance()->getMetavoxels()->applyEdit(message, true); + } + } } } @@ -1143,8 +1159,8 @@ void ImportHeightfieldTool::updatePreview() { float z = 0.0f; int blockSize = pow(2.0, _blockSize->value()); int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int colorScale = glm::round(glm::log(_colorImage.height() / _heightImage.height()) / glm::log(2.0f)); - int colorBlockSize = blockSize * pow(2.0, colorScale); + int colorScale = glm::round(glm::log(_colorImage.height() / (float)_heightImage.height()) / glm::log(2.0f)); + int colorBlockSize = blockSize * pow(2.0, qMax(colorScale, 0)); int colorSize = colorBlockSize + HeightfieldBuffer::SHARED_EDGE; for (int i = 0; i < _heightImage.height(); i += blockSize, z++) { float x = 0.0f; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index a625c71d6a..876864429d 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -1198,6 +1198,10 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) return false; } +AttributeValue HeightfieldAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); +} + HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) : InlineAttribute(name) { } @@ -1337,6 +1341,10 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post return false; } +AttributeValue HeightfieldColorAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); +} + HeightfieldMaterialAttribute::HeightfieldMaterialAttribute(const QString& name) : InlineAttribute(name) { } @@ -1404,6 +1412,10 @@ bool HeightfieldMaterialAttribute::merge(void*& parent, void* children[], bool p return maxSize == 0; } +AttributeValue HeightfieldMaterialAttribute::inherit(const AttributeValue& parentValue) const { + return AttributeValue(parentValue.getAttribute()); +} + const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6; static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ, diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index cee01cdbef..5caff17266 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -536,6 +536,8 @@ public: virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; virtual bool merge(void*& parent, void* children[], bool postRead = false) const; + + virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; typedef QExplicitlySharedDataPointer HeightfieldColorDataPointer; @@ -580,6 +582,8 @@ public: virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; virtual bool merge(void*& parent, void* children[], bool postRead = false) const; + + virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; typedef QExplicitlySharedDataPointer HeightfieldMaterialDataPointer; @@ -646,6 +650,8 @@ public: virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const; virtual bool merge(void*& parent, void* children[], bool postRead = false) const; + + virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; typedef QExplicitlySharedDataPointer VoxelColorDataPointer; From f0e4e034b66edd9c844387968100f28ffbbf66d7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 28 Oct 2014 18:55:12 -0700 Subject: [PATCH 05/50] Fix for color bytes. --- interface/src/MetavoxelSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b9baa356f5..5b34a068b5 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1970,7 +1970,7 @@ bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { signedLeftShift(_inheritedColorSize, _inheritedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, _containedColorDepth == -1 ? 0 : signedLeftShift(_containedColorSize, _containedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); - int colorContentsSize = extendedColorSize * extendedColorSize; + int colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES; int extendedMaterialSize = qMax(_inheritedMaterialDepth == -1 ? 0 : signedLeftShift(_inheritedMaterialSize, _inheritedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, From 37569dadd57d938998943a0fb10a92cba0e12599 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 29 Oct 2014 13:00:32 -0700 Subject: [PATCH 06/50] More resolution bits. --- interface/src/MetavoxelSystem.cpp | 97 +++++++++++++++++-- interface/src/MetavoxelSystem.h | 8 ++ .../metavoxels/src/AttributeRegistry.cpp | 61 ++++++++++++ libraries/metavoxels/src/AttributeRegistry.h | 7 ++ .../metavoxels/src/MetavoxelMessages.cpp | 61 ------------ 5 files changed, 164 insertions(+), 70 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 5b34a068b5..49f742450d 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -961,6 +961,7 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _scale(scale), _heightBounds(translation, translation + glm::vec3(scale, scale, scale)), _colorBounds(_heightBounds), + _materialBounds(_heightBounds), _height(height), _color(color), _material(material), @@ -968,10 +969,12 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _heightTextureID(0), _colorTextureID(0), _materialTextureID(0), - _heightSize(glm::sqrt(float(height.size()))), + _heightSize(glm::sqrt((float)height.size())), _heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)), - _colorSize(glm::sqrt(float(color.size() / DataBlock::COLOR_BYTES))), - _colorIncrement(scale / (_colorSize - SHARED_EDGE)) { + _colorSize(glm::sqrt((float)color.size() / DataBlock::COLOR_BYTES)), + _colorIncrement(scale / (_colorSize - SHARED_EDGE)), + _materialSize(glm::sqrt((float)material.size())), + _materialIncrement(scale / (_materialSize - SHARED_EDGE)) { _heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER; _heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER; @@ -980,6 +983,9 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE; _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE; + + _materialBounds.maximum.x += _colorIncrement * SHARED_EDGE; + _materialBounds.maximum.z += _colorIncrement * SHARED_EDGE; } HeightfieldBuffer::~HeightfieldBuffer() { @@ -1742,24 +1748,37 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - if (height || _heightDepth != -1) { - if (_depth < _heightDepth) { + if (height) { + // to handle borders correctly, make sure we only sample nodes with resolution <= ours + int heightSize = glm::sqrt((float)height->getContents().size()); + float heightIncrement = info.size / heightSize; + if (heightIncrement < _buffer->getHeightIncrement() || _depth < _heightDepth) { height.reset(); } + } + if (height || _heightDepth != -1) { _heightDepth = _depth; } HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); - if (color || _colorDepth != -1) { - if (_depth < _colorDepth) { + if (color) { + int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); + float colorIncrement = info.size / colorSize; + if (colorIncrement < _buffer->getColorIncrement() || _depth < _colorDepth) { color.reset(); } + } + if (color || _colorDepth != -1) { _colorDepth = _depth; } HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); - if (material || _materialDepth != -1) { - if (_depth < _materialDepth) { + if (material) { + int materialSize = glm::sqrt((float)material->getContents().size()); + float materialIncrement = info.size / materialSize; + if (materialIncrement < _buffer->getMaterialIncrement() || _depth < _materialDepth) { material.reset(); } + } + if (material || _materialDepth != -1) { _materialDepth = _depth; } if (!(height || color || material)) { @@ -1861,6 +1880,66 @@ bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { } } if (material) { + const Box& materialBounds = _buffer->getMaterialBounds(); + overlap = materialBounds.getIntersection(overlap); + float materialIncrement = _buffer->getMaterialIncrement(); + int destX = (overlap.minimum.x - materialBounds.minimum.x) / materialIncrement; + int destY = (overlap.minimum.z - materialBounds.minimum.z) / materialIncrement; + int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / materialIncrement); + int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / materialIncrement); + int materialSize = _buffer->getMaterialSize(); + char* dest = _buffer->getMaterial().data() + destY * materialSize + destX; + + const QByteArray& srcMaterial = material->getContents(); + const QVector srcMaterials = material->getMaterials(); + int srcSize = glm::sqrt((float)srcMaterial.size()); + float srcIncrement = info.size / srcSize; + QHash materialMappings; + + if (srcIncrement == materialIncrement) { + // easy case: same resolution + int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + + const uchar* src = (const uchar*)srcMaterial.constData() + srcY * srcSize + srcX; + for (int y = 0; y < destHeight; y++, src += srcSize, dest += materialSize) { + const uchar* lineSrc = src; + for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrc++) { + int value = *lineSrc; + if (value != 0) { + int& mapping = materialMappings[value]; + if (mapping == 0) { + mapping = getMaterialIndex(material->getMaterials().at(value - 1), + _buffer->getMaterials(), _buffer->getMaterial()); + } + value = mapping; + } + *lineDest = value; + } + } + } else { + // more difficult: different resolutions + float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement; + float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement; + float srcAdvance = materialIncrement / srcIncrement; + for (int y = 0; y < destHeight; y++, dest += materialSize, srcY += srcAdvance) { + const uchar* src = (const uchar*)srcMaterial.constData() + (int)srcY * srcSize; + float lineSrcX = srcX; + for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) { + int value = src[(int)lineSrcX]; + if (value != 0) { + int& mapping = materialMappings[value]; + if (mapping == 0) { + mapping = getMaterialIndex(material->getMaterials().at(value - 1), + _buffer->getMaterials(), _buffer->getMaterial()); + } + value = mapping; + } + *lineDest = value; + } + } + } + clearUnusedMaterials(_buffer->getMaterials(), _buffer->getMaterial()); } } return false; diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index 0dad7ee179..7e9f2ce2ca 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -236,6 +236,7 @@ public: const Box& getHeightBounds() const { return _heightBounds; } const Box& getColorBounds() const { return _colorBounds; } + const Box& getMaterialBounds() const { return _materialBounds; } QByteArray& getHeight() { return _height; } const QByteArray& getHeight() const { return _height; } @@ -246,6 +247,7 @@ public: QByteArray& getMaterial() { return _material; } const QByteArray& getMaterial() const { return _material; } + QVector& getMaterials() { return _materials; } const QVector& getMaterials() const { return _materials; } QByteArray getUnextendedHeight() const; @@ -257,6 +259,9 @@ public: int getColorSize() const { return _colorSize; } float getColorIncrement() const { return _colorIncrement; } + int getMaterialSize() const { return _materialSize; } + float getMaterialIncrement() const { return _materialIncrement; } + virtual void render(bool cursor = false); private: @@ -265,6 +270,7 @@ private: float _scale; Box _heightBounds; Box _colorBounds; + Box _materialBounds; QByteArray _height; QByteArray _color; QByteArray _material; @@ -277,6 +283,8 @@ private: float _heightIncrement; int _colorSize; float _colorIncrement; + int _materialSize; + float _materialIncrement; typedef QPair BufferPair; static QHash _bufferPairs; diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 876864429d..c06e376a25 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -1416,6 +1416,67 @@ AttributeValue HeightfieldMaterialAttribute::inherit(const AttributeValue& paren return AttributeValue(parentValue.getAttribute()); } +static QHash countIndices(const QByteArray& contents) { + QHash counts; + for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { + if (*src != 0) { + counts[*src]++; + } + } + return counts; +} + +uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents) { + if (!(material && static_cast(material.data())->getDiffuse().isValid())) { + return 0; + } + // first look for a matching existing material, noting the first reusable slot + int firstEmptyIndex = -1; + for (int i = 0; i < materials.size(); i++) { + const SharedObjectPointer& existingMaterial = materials.at(i); + if (existingMaterial) { + if (existingMaterial->equals(material.data())) { + return i + 1; + } + } else if (firstEmptyIndex == -1) { + firstEmptyIndex = i; + } + } + // if nothing found, use the first empty slot or append + if (firstEmptyIndex != -1) { + materials[firstEmptyIndex] = material; + return firstEmptyIndex + 1; + } + if (materials.size() < EIGHT_BIT_MAXIMUM) { + materials.append(material); + return materials.size(); + } + // last resort: find the least-used material and remove it + QHash counts = countIndices(contents); + uchar materialIndex = 0; + int lowestCount = INT_MAX; + for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { + if (it.value() < lowestCount) { + materialIndex = it.key(); + lowestCount = it.value(); + } + } + contents.replace((char)materialIndex, (char)0); + return materialIndex; +} + +void clearUnusedMaterials(QVector& materials, const QByteArray& contents) { + QHash counts = countIndices(contents); + for (int i = 0; i < materials.size(); i++) { + if (counts.value(i + 1) == 0) { + materials[i] = SharedObjectPointer(); + } + } + while (!(materials.isEmpty() || materials.last())) { + materials.removeLast(); + } +} + const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6; static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ, diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 5caff17266..945c4df94b 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -654,6 +654,13 @@ public: virtual AttributeValue inherit(const AttributeValue& parentValue) const; }; +/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index, +/// creating a new entry in the list if necessary. +uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents); + +/// Utility method for editing: removes any unused materials from the supplied list. +void clearUnusedMaterials(QVector& materials, const QByteArray& contents); + typedef QExplicitlySharedDataPointer VoxelColorDataPointer; /// Contains a block of voxel color data. diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 549931e030..4c6611f4bd 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -449,67 +449,6 @@ PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const g _bounds = Box(_position - extents, _position + extents); } -static QHash countIndices(const QByteArray& contents) { - QHash counts; - for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) { - if (*src != 0) { - counts[*src]++; - } - } - return counts; -} - -uchar getMaterialIndex(const SharedObjectPointer& material, QVector& materials, QByteArray& contents) { - if (!(material && static_cast(material.data())->getDiffuse().isValid())) { - return 0; - } - // first look for a matching existing material, noting the first reusable slot - int firstEmptyIndex = -1; - for (int i = 0; i < materials.size(); i++) { - const SharedObjectPointer& existingMaterial = materials.at(i); - if (existingMaterial) { - if (existingMaterial->equals(material.data())) { - return i + 1; - } - } else if (firstEmptyIndex == -1) { - firstEmptyIndex = i; - } - } - // if nothing found, use the first empty slot or append - if (firstEmptyIndex != -1) { - materials[firstEmptyIndex] = material; - return firstEmptyIndex + 1; - } - if (materials.size() < EIGHT_BIT_MAXIMUM) { - materials.append(material); - return materials.size(); - } - // last resort: find the least-used material and remove it - QHash counts = countIndices(contents); - uchar materialIndex = 0; - int lowestCount = INT_MAX; - for (QHash::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) { - if (it.value() < lowestCount) { - materialIndex = it.key(); - lowestCount = it.value(); - } - } - contents.replace((char)materialIndex, (char)0); - return materialIndex; -} - -void clearUnusedMaterials(QVector& materials, QByteArray& contents) { - QHash counts = countIndices(contents); - for (int i = 0; i < materials.size(); i++) { - if (counts.value(i + 1) == 0) { - materials[i] = SharedObjectPointer(); - } - } - while (!(materials.isEmpty() || materials.last())) { - materials.removeLast(); - } -} - int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_bounds)) { return STOP_RECURSION; From 754d1a28029667ce6292a4c277fabf6a9d5e7f73 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 29 Oct 2014 16:55:55 -0700 Subject: [PATCH 07/50] Another resolution fix. --- interface/src/MetavoxelSystem.cpp | 41 +++++++++++++++++-------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 49f742450d..d89725e126 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -984,8 +984,8 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale, _colorBounds.maximum.x += _colorIncrement * SHARED_EDGE; _colorBounds.maximum.z += _colorIncrement * SHARED_EDGE; - _materialBounds.maximum.x += _colorIncrement * SHARED_EDGE; - _materialBounds.maximum.z += _colorIncrement * SHARED_EDGE; + _materialBounds.maximum.x += _materialIncrement * SHARED_EDGE; + _materialBounds.maximum.z += _materialIncrement * SHARED_EDGE; } HeightfieldBuffer::~HeightfieldBuffer() { @@ -1716,11 +1716,11 @@ private: const QVector& _intersections; HeightfieldBuffer* _buffer; - int _heightDepth; - int _colorDepth; - int _materialDepth; + QVector _depthFlags; }; +enum DepthFlags { HEIGHT_FLAG = 0x01, COLOR_FLAG = 0x02, MATERIAL_FLAG = 0x04 }; + HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector& intersections) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getHeightfieldAttribute() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() << @@ -1730,15 +1730,17 @@ HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const void HeightfieldFetchVisitor::init(HeightfieldBuffer* buffer) { _buffer = buffer; - _heightDepth = -1; - _colorDepth = -1; - _materialDepth = -1; } int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { if (!info.getBounds().intersects(_buffer->getHeightBounds())) { return STOP_RECURSION; } + if (_depthFlags.size() > _depth) { + _depthFlags[_depth] = 0; + } else { + _depthFlags.append(0); + } if (!info.isLeaf) { return DEFAULT_ORDER; } @@ -1748,38 +1750,39 @@ int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) { bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) { HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); + int flags = _depthFlags.at(_depth); if (height) { // to handle borders correctly, make sure we only sample nodes with resolution <= ours int heightSize = glm::sqrt((float)height->getContents().size()); float heightIncrement = info.size / heightSize; - if (heightIncrement < _buffer->getHeightIncrement() || _depth < _heightDepth) { + if (heightIncrement < _buffer->getHeightIncrement() || (flags & HEIGHT_FLAG)) { height.reset(); + } else { + flags |= HEIGHT_FLAG; } } - if (height || _heightDepth != -1) { - _heightDepth = _depth; - } HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); if (color) { int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); float colorIncrement = info.size / colorSize; - if (colorIncrement < _buffer->getColorIncrement() || _depth < _colorDepth) { + if (colorIncrement < _buffer->getColorIncrement() || (flags & COLOR_FLAG)) { color.reset(); + } else { + flags |= COLOR_FLAG; } } - if (color || _colorDepth != -1) { - _colorDepth = _depth; - } HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); if (material) { int materialSize = glm::sqrt((float)material->getContents().size()); float materialIncrement = info.size / materialSize; - if (materialIncrement < _buffer->getMaterialIncrement() || _depth < _materialDepth) { + if (materialIncrement < _buffer->getMaterialIncrement() || (flags & MATERIAL_FLAG)) { material.reset(); + } else { + flags |= MATERIAL_FLAG; } } - if (material || _materialDepth != -1) { - _materialDepth = _depth; + if (_depth > 0) { + _depthFlags[_depth - 1] |= flags; } if (!(height || color || material)) { return false; From 1c4e71ebc1526b669cc25fcc0d6cc6e18da4441b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 30 Oct 2014 14:33:10 -0700 Subject: [PATCH 08/50] Revert "Worklist #20074" --- interface/src/Application.cpp | 3 - interface/src/Menu.cpp | 6 -- interface/src/Menu.h | 3 - interface/src/devices/OculusManager.h | 1 - interface/src/ui/PreferencesDialog.cpp | 4 - interface/ui/preferencesDialog.ui | 113 ++++--------------------- 6 files changed, 17 insertions(+), 113 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 92e74a9a2a..0752f9fa9e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1558,12 +1558,9 @@ void Application::setEnableVRMode(bool enableVRMode) { OculusManager::disconnect(); OculusManager::connect(); } - int oculusMaxFPS = Menu::getInstance()->getOculusUIMaxFPS(); - setRenderTargetFramerate(oculusMaxFPS); OculusManager::recalibrate(); } else { OculusManager::abandonCalibration(); - setRenderTargetFramerate(0); } resizeGL(_glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f2e2d2b3d9..002daf29d5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -105,7 +105,6 @@ Menu::Menu() : _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), _oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE), - _oculusUIMaxFPS(DEFAULT_OCULUS_UI_MAX_FPS), _sixenseReticleMoveSpeed(DEFAULT_SIXENSE_RETICLE_MOVE_SPEED), _invertSixenseButtons(DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS), _automaticAvatarLOD(true), @@ -784,8 +783,6 @@ void Menu::loadSettings(QSettings* settings) { settings->endGroup(); _walletPrivateKey = settings->value("privateKey").toByteArray(); - - _oculusUIMaxFPS = loadSetting(settings, "oculusUIMaxFPS", 0.0f); scanMenuBar(&loadAction, settings); Application::getInstance()->getAvatar()->loadData(settings); @@ -847,9 +844,6 @@ void Menu::saveSettings(QSettings* settings) { settings->setValue("viewFrustumOffsetUp", _viewFrustumOffset.up); settings->endGroup(); settings->setValue("privateKey", _walletPrivateKey); - - // Oculus Rift settings - settings->setValue("oculusUIMaxFPS", _oculusUIMaxFPS); scanMenuBar(&saveAction, settings); Application::getInstance()->getAvatar()->saveData(settings); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d5ffbfcfc0..009d1e3097 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -100,8 +100,6 @@ public: void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; bumpSettings(); } float getOculusUIAngularSize() const { return _oculusUIAngularSize; } void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; bumpSettings(); } - int getOculusUIMaxFPS() const { return _oculusUIMaxFPS; } - void setOculusUIMaxFPS(int oculusUIMaxFPS) { _oculusUIMaxFPS = oculusUIMaxFPS; bumpSettings(); } float getSixenseReticleMoveSpeed() const { return _sixenseReticleMoveSpeed; } void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; bumpSettings(); } bool getInvertSixenseButtons() const { return _invertSixenseButtons; } @@ -295,7 +293,6 @@ private: int _maxVoxels; float _voxelSizeScale; float _oculusUIAngularSize; - int _oculusUIMaxFPS; float _sixenseReticleMoveSpeed; bool _invertSixenseButtons; bool _automaticAvatarLOD; diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 94521dc927..20e43d572c 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -21,7 +21,6 @@ #include "ui/overlays/Text3DOverlay.h" const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f; -const int DEFAULT_OCULUS_UI_MAX_FPS = 75; class Camera; class PalmData; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 42256bc498..61cc9718b3 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -147,8 +147,6 @@ void PreferencesDialog::loadPreferences() { ui.maxVoxelsPPSSpin->setValue(menuInstance->getMaxVoxelPacketsPerSecond()); ui.oculusUIAngularSizeSpin->setValue(menuInstance->getOculusUIAngularSize()); - - ui.oculusUIMaxFPSSpin->setValue(menuInstance->getOculusUIMaxFPS()); ui.sixenseReticleMoveSpeedSpin->setValue(menuInstance->getSixenseReticleMoveSpeed()); @@ -231,8 +229,6 @@ void PreferencesDialog::savePreferences() { Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value()); Menu::getInstance()->setOculusUIAngularSize(ui.oculusUIAngularSizeSpin->value()); - - Menu::getInstance()->setOculusUIMaxFPS(ui.oculusUIMaxFPSSpin->value()); Menu::getInstance()->setSixenseReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index 2dda1a24b1..0108437c1f 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -61,7 +61,7 @@ 0 0 500 - 1459 + 1386 @@ -1772,24 +1772,24 @@ - - - Arial - - - - Qt::LeftToRight - - - - - - localhost - - + + + Arial + + + + Qt::LeftToRight + + + + + + localhost + + - + @@ -2084,85 +2084,6 @@ - - - - 0 - - - 7 - - - 0 - - - 7 - - - - - - Arial - - - - Oculus Rift FPS - - - 0 - - - maxVoxelsSpin - - - - - - - - Arial - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 100 - 0 - - - - - Arial - - - - 30 - - - 95 - - - 1 - - - 75 - - - - - From 81cc15495f7cde1505b2a4bbb5ef2653150c1168 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 30 Oct 2014 14:43:43 -0700 Subject: [PATCH 09/50] rename Models to Entities, and add menu option to disable Light entities --- examples/lobby.js | 2 +- interface/src/Application.cpp | 4 ++-- interface/src/Menu.cpp | 20 ++++++++++--------- interface/src/Menu.h | 4 ++-- .../entities/RenderableLightEntityItem.cpp | 19 ++++++++++-------- .../src/voxels/OctreePacketProcessor.cpp | 4 ++-- 6 files changed, 29 insertions(+), 24 deletions(-) diff --git a/examples/lobby.js b/examples/lobby.js index 4642d13387..b5353131a5 100644 --- a/examples/lobby.js +++ b/examples/lobby.js @@ -174,7 +174,7 @@ function maybeCleanupLobby() { function toggleEnvironmentRendering(shouldRender) { Menu.setIsOptionChecked("Voxels", shouldRender); - Menu.setIsOptionChecked("Models", shouldRender); + Menu.setIsOptionChecked("Entities", shouldRender); Menu.setIsOptionChecked("Metavoxels", shouldRender); Menu.setIsOptionChecked("Avatars", shouldRender); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 92e74a9a2a..c33bc17091 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2338,7 +2338,7 @@ void Application::update(float deltaTime) { if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions); } - if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Entities)) { queryOctree(NodeType::EntityServer, PacketTypeEntityQuery, _entityServerJurisdictions); } _lastQueriedViewFrustum = _viewFrustum; @@ -2985,7 +2985,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } // render models... - if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Entities)) { PerformanceTimer perfTimer("entities"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... entities..."); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f2e2d2b3d9..617a119aad 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -367,7 +367,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Avatars, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Models, 0, true); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Entities, 0, true); QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows"); QActionGroup* shadowGroup = new QActionGroup(shadowMenu); @@ -434,14 +434,16 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); - QMenu* modelDebugMenu = developerMenu->addMenu("Models"); - addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelBounds, 0, false); - addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementProxy, 0, false); - addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementChildProxies, 0, false); - QMenu* modelCullingMenu = modelDebugMenu->addMenu("Culling"); - addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontCullOutOfViewMeshParts, 0, false); - addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontCullTooSmallMeshParts, 0, false); - addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontReduceMaterialSwitches, 0, false); + QMenu* entitiesDebugMenu = developerMenu->addMenu("Entities"); + addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisplayModelBounds, 0, false); + addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisplayModelElementProxy, 0, false); + addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisplayModelElementChildProxies, 0, false); + addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisableLightEntities, 0, false); + + QMenu* entityCullingMenu = entitiesDebugMenu->addMenu("Culling"); + addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontCullOutOfViewMeshParts, 0, false); + addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontCullTooSmallMeshParts, 0, false); + addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontReduceMaterialSwitches, 0, false); QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxels"); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d5ffbfcfc0..6afd03a0f0 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -387,6 +387,7 @@ namespace MenuOption { const QString DecreaseVoxelSize = "Decrease Voxel Size"; const QString DisableActivityLogger = "Disable Activity Logger"; const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD"; + const QString DisableLightEntities = "Disable Light Entities"; const QString DisableNackPackets = "Disable NACK Packets"; const QString DisplayFrustum = "Display Frustum"; const QString DisplayHands = "Show Hand Info"; @@ -403,6 +404,7 @@ namespace MenuOption { const QString Enable3DTVMode = "Enable 3DTV Mode"; const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)"; const QString EnableVRMode = "Enable VR Mode"; + const QString Entities = "Entities"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; @@ -433,8 +435,6 @@ namespace MenuOption { const QString MetavoxelEditor = "Metavoxel Editor..."; const QString Metavoxels = "Metavoxels"; const QString Mirror = "Mirror"; - const QString ModelOptions = "Model Options"; - const QString Models = "Models"; const QString MoveWithLean = "Move with Lean"; const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; diff --git a/interface/src/entities/RenderableLightEntityItem.cpp b/interface/src/entities/RenderableLightEntityItem.cpp index bf9939e164..e3e8f61e58 100644 --- a/interface/src/entities/RenderableLightEntityItem.cpp +++ b/interface/src/entities/RenderableLightEntityItem.cpp @@ -61,15 +61,18 @@ void RenderableLightEntityItem::render(RenderArgs* args) { float exponent = getExponent(); float cutoff = glm::radians(getCutoff()); - if (_isSpotlight) { - Application::getInstance()->getDeferredLightingEffect()->addSpotLight(position, largestDiameter / 2.0f, - ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation, - direction, exponent, cutoff); - } else { - Application::getInstance()->getDeferredLightingEffect()->addPointLight(position, largestDiameter / 2.0f, - ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation); - } + bool disableLights = Menu::getInstance()->isOptionChecked(MenuOption::DisableLightEntities); + if (!disableLights) { + if (_isSpotlight) { + Application::getInstance()->getDeferredLightingEffect()->addSpotLight(position, largestDiameter / 2.0f, + ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation, + direction, exponent, cutoff); + } else { + Application::getInstance()->getDeferredLightingEffect()->addPointLight(position, largestDiameter / 2.0f, + ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation); + } + } bool wantDebug = false; if (wantDebug) { glColor4f(diffuseR, diffuseG, diffuseB, 1.0f); diff --git a/interface/src/voxels/OctreePacketProcessor.cpp b/interface/src/voxels/OctreePacketProcessor.cpp index b3cf012869..9f53492e70 100644 --- a/interface/src/voxels/OctreePacketProcessor.cpp +++ b/interface/src/voxels/OctreePacketProcessor.cpp @@ -86,13 +86,13 @@ void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode, switch(voxelPacketType) { case PacketTypeEntityErase: { - if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Entities)) { app->_entities.processEraseMessage(mutablePacket, sendingNode); } } break; case PacketTypeEntityData: { - if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Entities)) { app->_entities.processDatagram(mutablePacket, sendingNode); } } break; From 915a0d52d42010d7a3537cf60807028bae1a27f1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 30 Oct 2014 15:01:59 -0700 Subject: [PATCH 10/50] only remap textures on the render thread, fix deadlock, and improve handling of changes to textures --- .../entities/RenderableModelEntityItem.cpp | 61 +++++++++++++------ .../src/entities/RenderableModelEntityItem.h | 2 + 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/interface/src/entities/RenderableModelEntityItem.cpp b/interface/src/entities/RenderableModelEntityItem.cpp index 5a3ee61bbe..b64bca4217 100644 --- a/interface/src/entities/RenderableModelEntityItem.cpp +++ b/interface/src/entities/RenderableModelEntityItem.cpp @@ -38,12 +38,9 @@ RenderableModelEntityItem::~RenderableModelEntityItem() { bool RenderableModelEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) { QString oldModelURL = getModelURL(); - QString oldTextures = getTextures(); bool somethingChanged = ModelEntityItem::setProperties(properties, forceCopy); - if (somethingChanged) { - if ((oldModelURL != getModelURL()) || (oldTextures != getTextures())) { - _needsModelReload = true; - } + if (somethingChanged && oldModelURL != getModelURL()) { + _needsModelReload = true; } return somethingChanged; } @@ -60,6 +57,47 @@ int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned c return bytesRead; } +void RenderableModelEntityItem::remapTextures() { + if (!_model) { + return; // nothing to do if we don't have a model + } + + if (_currentTextures == _textures) { + return; // nothing to do if our recently mapped textures match our desired textures + } + qDebug() << "void RenderableModelEntityItem::remapTextures()...."; + + // since we're changing here, we need to run through our current texture map + // and any textures in the recently mapped texture, that is not in our desired + // textures, we need to "unset" + QJsonDocument currentTexturesAsJson = QJsonDocument::fromJson(_currentTextures.toUtf8()); + QJsonObject currentTexturesAsJsonObject = currentTexturesAsJson.object(); + QVariantMap currentTextureMap = currentTexturesAsJsonObject.toVariantMap(); + + QJsonDocument texturesAsJson = QJsonDocument::fromJson(_textures.toUtf8()); + QJsonObject texturesAsJsonObject = texturesAsJson.object(); + QVariantMap textureMap = texturesAsJsonObject.toVariantMap(); + + foreach(const QString& key, currentTextureMap.keys()) { + // if the desired texture map (what we're setting the textures to) doesn't + // contain this texture, then remove it by setting the URL to null + if (!textureMap.contains(key)) { + QUrl noURL; + qDebug() << "Removing texture named" << key << "by replacing it with no URL"; + _model->setTextureWithNameToURL(key, noURL); + } + } + + // here's where we remap any textures if needed... + foreach(const QString& key, textureMap.keys()) { + QUrl newTextureURL = textureMap[key].toUrl(); + qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL; + _model->setTextureWithNameToURL(key, newTextureURL); + } + + _currentTextures = _textures; +} + void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); @@ -72,6 +110,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE; if (drawAsModel) { + remapTextures(); glPushMatrix(); { float alpha = getLocalRenderAlpha(); @@ -183,18 +222,6 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { } } - // here's where we remap any textures if needed... - if (!_textures.isEmpty() && _model) { - QJsonDocument texturesAsJson = QJsonDocument::fromJson(_textures.toUtf8()); - QJsonObject texturesAsJsonObject = texturesAsJson.object(); - QVariantMap textureMap = texturesAsJsonObject.toVariantMap(); - foreach(const QString& key, textureMap.keys()) { - QUrl newTextureURL = textureMap[key].toUrl(); - qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL; - _model->setTextureWithNameToURL(key, newTextureURL); - } - } - return result; } diff --git a/interface/src/entities/RenderableModelEntityItem.h b/interface/src/entities/RenderableModelEntityItem.h index f36b66dba4..dbc77b7e48 100644 --- a/interface/src/entities/RenderableModelEntityItem.h +++ b/interface/src/entities/RenderableModelEntityItem.h @@ -51,12 +51,14 @@ public: virtual void render(RenderArgs* args); Model* getModel(EntityTreeRenderer* renderer); private: + void remapTextures(); bool needsSimulation() const; Model* _model; bool _needsInitialSimulation; bool _needsModelReload; EntityTreeRenderer* _myRenderer; + QString _currentTextures; }; #endif // hifi_RenderableModelEntityItem_h From 5e21701a5b0c49658ff7c658f722ded4fc0ee478 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 30 Oct 2014 15:22:50 -0700 Subject: [PATCH 11/50] fix possible deadlocks --- interface/src/entities/RenderableModelEntityItem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/entities/RenderableModelEntityItem.cpp b/interface/src/entities/RenderableModelEntityItem.cpp index b64bca4217..ce8d497da3 100644 --- a/interface/src/entities/RenderableModelEntityItem.cpp +++ b/interface/src/entities/RenderableModelEntityItem.cpp @@ -198,6 +198,10 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { } assert(_myRenderer == renderer); // you should only ever render on one renderer + if (QThread::currentThread() != _myRenderer->thread()) { + return _model; + } + _needsModelReload = false; // this is the reload // if we have a URL, then we will want to end up returning a model... From fecea5fe3c0ec6dfc84a6bb84a08d1dde44bab76 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 30 Oct 2014 15:27:05 -0700 Subject: [PATCH 12/50] fixes to the Model List feature in editModels.js and added it to new edit --- examples/editModels.js | 18 ++++++++++++------ examples/newEditEntities.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index d0e69807aa..7f4c27814c 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -2787,13 +2787,13 @@ function setupModelMenus() { print("delete exists... don't add ours"); } + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L", afterItem: "Paste Models", isCheckable: true }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S", afterItem: "Allow Select Large Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List", afterItem: "Models" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" }); @@ -2809,6 +2809,7 @@ function cleanupModelMenus() { Menu.removeMenuItem("Edit", "Delete"); } + Menu.removeMenuItem("Edit", "Model List..."); Menu.removeMenuItem("Edit", "Paste Models"); Menu.removeMenuItem("Edit", "Allow Select Large Models"); Menu.removeMenuItem("Edit", "Allow Select Small Models"); @@ -2883,16 +2884,21 @@ function handeMenuEvent(menuItem) { } else { print(" Delete Entity.... not holding..."); } - } else if (menuItem == "Model List") { + } else if (menuItem == "Model List...") { var models = new Array(); models = Entities.findEntities(MyAvatar.position, Number.MAX_VALUE); for (var i = 0; i < models.length; i++) { models[i].properties = Entities.getEntityProperties(models[i]); models[i].toString = function() { - var modelname = decodeURIComponent( - this.properties.modelURL.indexOf("/") != -1 ? - this.properties.modelURL.substring(this.properties.modelURL.lastIndexOf("/") + 1) : - this.properties.modelURL); + var modelname; + if (this.properties.type == "Model") { + modelname = decodeURIComponent( + this.properties.modelURL.indexOf("/") != -1 ? + this.properties.modelURL.substring(this.properties.modelURL.lastIndexOf("/") + 1) : + this.properties.modelURL); + } else { + modelname = this.properties.id; + } return "[" + this.properties.type + "] " + modelname; }; } diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index 8d19a350a2..71e29b3a38 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -584,6 +584,7 @@ function setupModelMenus() { print("delete exists... don't add ours"); } + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L", afterItem: "Paste Models", isCheckable: true }); @@ -606,6 +607,7 @@ function cleanupModelMenus() { Menu.removeMenuItem("Edit", "Delete"); } + Menu.removeMenuItem("Edit", "Model List..."); Menu.removeMenuItem("Edit", "Paste Models"); Menu.removeMenuItem("Edit", "Allow Select Large Models"); Menu.removeMenuItem("Edit", "Allow Select Small Models"); @@ -662,6 +664,38 @@ function handeMenuEvent(menuItem) { } else { print(" Delete Entity.... not holding..."); } + } else if (menuItem == "Model List...") { + var models = new Array(); + models = Entities.findEntities(MyAvatar.position, Number.MAX_VALUE); + for (var i = 0; i < models.length; i++) { + models[i].properties = Entities.getEntityProperties(models[i]); + models[i].toString = function() { + var modelname; + if (this.properties.type == "Model") { + modelname = decodeURIComponent( + this.properties.modelURL.indexOf("/") != -1 ? + this.properties.modelURL.substring(this.properties.modelURL.lastIndexOf("/") + 1) : + this.properties.modelURL); + } else { + modelname = this.properties.id; + } + return "[" + this.properties.type + "] " + modelname; + }; + } + var form = [{label: "Model: ", options: models}]; + form.push({label: "Action: ", options: ["Properties", "Delete", "Teleport"]}); + form.push({ button: "Cancel" }); + if (Window.form("Model List", form)) { + var selectedModel = form[0].value; + if (form[1].value == "Properties") { + editModelID = selectedModel; + showPropertiesForm(editModelID); + } else if (form[1].value == "Delete") { + Entities.deleteEntity(selectedModel); + } else if (form[1].value == "Teleport") { + MyAvatar.position = selectedModel.properties.position; + } + } } else if (menuItem == "Edit Properties...") { // good place to put the properties dialog From fd12c7a0d7a03ac63435a6a788bb9eba6e78ff21 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 30 Oct 2014 16:52:19 -0700 Subject: [PATCH 13/50] One more piece of the resolution puzzle. --- interface/src/MetavoxelSystem.cpp | 187 +++++++++------------ libraries/metavoxels/src/MetavoxelData.cpp | 31 ++-- libraries/metavoxels/src/MetavoxelData.h | 2 + 3 files changed, 104 insertions(+), 116 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index d89725e126..4c0acc553a 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1963,20 +1963,15 @@ private: void addRegion(const Box& unextended, const Box& extended); - int _heightSize; - int _heightDepth; + class DepthInfo { + public: + float minimumColorIncrement; + float minimumMaterialIncrement; + + DepthInfo() : minimumColorIncrement(FLT_MAX), minimumMaterialIncrement(FLT_MAX) { } + }; - int _inheritedColorSize; - int _inheritedColorDepth; - - int _containedColorSize; - int _containedColorDepth; - - int _inheritedMaterialSize; - int _inheritedMaterialDepth; - - int _containedMaterialSize; - int _containedMaterialDepth; + QVector _depthInfo; QVector _intersections; HeightfieldFetchVisitor _fetchVisitor; @@ -1989,118 +1984,100 @@ HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) : Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), QVector() << Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod), regionBounds(glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX), glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX)), - _heightDepth(-1), - _inheritedColorDepth(-1), - _containedColorDepth(-1), - _inheritedMaterialDepth(-1), - _containedMaterialDepth(-1), _fetchVisitor(lod, _intersections) { } -static int signedLeftShift(int value, int shift) { - return (shift > 0) ? value << shift : value >> (-shift); -} - int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) { - HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); - int order = DEFAULT_ORDER; - if (height) { - _heightSize = glm::sqrt((float)height->getContents().size()); - _heightDepth = _depth; - order |= ALL_NODES_REST; - } + DepthInfo depthInfo; HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue(); if (color) { - int colorSize = glm::sqrt((float)(color->getContents().size() / DataBlock::COLOR_BYTES)); - if (_heightDepth == -1) { - _inheritedColorSize = colorSize; - _inheritedColorDepth = _depth; - - } else if (_containedColorDepth == -1 || - colorSize > signedLeftShift(_containedColorSize, _containedColorDepth - _depth)) { - _containedColorSize = colorSize; - _containedColorDepth = _depth; - } + int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES); + depthInfo.minimumColorIncrement = info.size / colorSize; } HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue(); if (material) { - int materialSize = glm::sqrt((float)material->getContents().size()); - if (_heightDepth == -1) { - _inheritedMaterialSize = materialSize; - _inheritedMaterialDepth = _depth; - - } else if (_containedMaterialDepth == -1 || - materialSize > signedLeftShift(_containedMaterialSize, _containedMaterialDepth - _depth)) { - _containedMaterialSize = materialSize; - _containedMaterialDepth = _depth; - } + int materialSize = glm::sqrt((float)material->getContents().size()); + depthInfo.minimumMaterialIncrement = info.size / materialSize; + } + if (_depth < _depthInfo.size()) { + _depthInfo[_depth] = depthInfo; + } else { + _depthInfo.append(depthInfo); } if (!info.isLeaf) { - return order; + return _visitations.at(_depth).isInputLeaf(0) ? (DEFAULT_ORDER | ALL_NODES_REST) : DEFAULT_ORDER; } postVisit(info); return STOP_RECURSION; } bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) { - HeightfieldBuffer* buffer = NULL; - if (_depth == _heightDepth) { - int extendedHeightSize = _heightSize + HeightfieldBuffer::HEIGHT_EXTENSION; - int heightContentsSize = extendedHeightSize * extendedHeightSize; - - int extendedColorSize = qMax(_inheritedColorDepth == -1 ? 0 : - signedLeftShift(_inheritedColorSize, _inheritedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, - _containedColorDepth == -1 ? 0 : - signedLeftShift(_containedColorSize, _containedColorDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); - int colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES; - - int extendedMaterialSize = qMax(_inheritedMaterialDepth == -1 ? 0 : - signedLeftShift(_inheritedMaterialSize, _inheritedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE, - _containedMaterialDepth == -1 ? 0 : - signedLeftShift(_containedMaterialSize, _containedMaterialDepth - _depth) + HeightfieldBuffer::SHARED_EDGE); - int materialContentsSize = extendedMaterialSize * extendedMaterialSize; - - const HeightfieldBuffer* existingBuffer = static_cast( - info.inputValues.at(3).getInlineValue().data()); - Box bounds = info.getBounds(); - if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && - existingBuffer->getColor().size() == colorContentsSize && - existingBuffer->getMaterial().size() == materialContentsSize) { - // we already have a buffer of the correct resolution - addRegion(bounds, existingBuffer->getHeightBounds()); - buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), - existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials()); - - } else { - // we must create a new buffer and update its borders - buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), - QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0)); - const Box& heightBounds = buffer->getHeightBounds(); - addRegion(bounds, heightBounds); + const DepthInfo& depthInfo = _depthInfo.at(_depth); + if (_depth > 0) { + DepthInfo& parentDepthInfo = _depthInfo[_depth - 1]; + parentDepthInfo.minimumColorIncrement = qMin(parentDepthInfo.minimumColorIncrement, depthInfo.minimumColorIncrement); + parentDepthInfo.minimumMaterialIncrement = qMin(parentDepthInfo.minimumMaterialIncrement, + depthInfo.minimumMaterialIncrement); + } + if (_visitations.at(_depth).isInputLeaf(0)) { + HeightfieldBuffer* buffer = NULL; + HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue(); + if (height) { + int heightSize = glm::sqrt((float)height->getContents().size()); + int extendedHeightSize = heightSize + HeightfieldBuffer::HEIGHT_EXTENSION; + int heightContentsSize = extendedHeightSize * extendedHeightSize; + float minimumColorIncrement = depthInfo.minimumColorIncrement; + float minimumMaterialIncrement = depthInfo.minimumMaterialIncrement; + for (int i = _depth - 1; i >= 0 && qMax(minimumColorIncrement, minimumMaterialIncrement) == FLT_MAX; i--) { + const DepthInfo& ancestorDepthInfo = _depthInfo.at(i); + minimumColorIncrement = qMin(minimumColorIncrement, ancestorDepthInfo.minimumColorIncrement); + minimumMaterialIncrement = qMin(minimumMaterialIncrement, ancestorDepthInfo.minimumMaterialIncrement); + } + int colorContentsSize = 0; + if (minimumColorIncrement != FLT_MAX) { + int colorSize = (int)glm::round(info.size / minimumColorIncrement) + HeightfieldBuffer::SHARED_EDGE; + colorContentsSize = colorSize * colorSize * DataBlock::COLOR_BYTES; + } + int materialContentsSize = 0; + if (minimumMaterialIncrement != FLT_MAX) { + int materialSize = (int)glm::round(info.size / minimumMaterialIncrement) + HeightfieldBuffer::SHARED_EDGE; + materialContentsSize = materialSize * materialSize; + } + const HeightfieldBuffer* existingBuffer = static_cast( + info.inputValues.at(3).getInlineValue().data()); + Box bounds = info.getBounds(); + if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize && + existingBuffer->getColor().size() == colorContentsSize && + existingBuffer->getMaterial().size() == materialContentsSize) { + // we already have a buffer of the correct resolution + addRegion(bounds, existingBuffer->getHeightBounds()); + buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(), + existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials()); - _intersections.clear(); - _intersections.append(Box(heightBounds.minimum, - glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z))); - _intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z), - glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z))); - _intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z), - heightBounds.maximum)); - _intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z), - glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z))); - - _fetchVisitor.init(buffer); - _data->guide(_fetchVisitor); + } else { + // we must create a new buffer and update its borders + buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0), + QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0)); + const Box& heightBounds = buffer->getHeightBounds(); + addRegion(bounds, heightBounds); + + _intersections.clear(); + _intersections.append(Box(heightBounds.minimum, + glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z))); + _intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z), + glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z))); + _intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z), + heightBounds.maximum)); + _intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z), + glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z))); + + _fetchVisitor.init(buffer); + _data->guide(_fetchVisitor); + } } - _heightDepth = _containedColorDepth = _containedMaterialDepth = -1; + BufferDataPointer pointer(buffer); + info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); } - if (_depth == _inheritedColorDepth) { - _inheritedColorDepth = -1; - } - if (_depth == _inheritedMaterialDepth) { - _inheritedMaterialDepth = -1; - } - BufferDataPointer pointer(buffer); - info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer)); return true; } diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 2f43f8f4ae..b79903ba8a 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -1509,7 +1509,7 @@ bool MetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { DefaultMetavoxelGuide::DefaultMetavoxelGuide() { } -static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float lodBase, int encodedOrder) { +static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, int encodedOrder) { MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation(); nextVisitation.info.size = visitation.info.size * 0.5f; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { @@ -1519,14 +1519,14 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float for (int j = 0; j < visitation.inputNodes.size(); j++) { MetavoxelNode* node = visitation.inputNodes.at(j); const AttributeValue& parentValue = visitation.info.inputValues.at(j); - MetavoxelNode* child = (node && (visitation.info.size >= lodBase * + MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase * parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue); } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = (node && (visitation.info.size >= lodBase * + MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase * visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.outputNodes[j] = child; } @@ -1604,9 +1604,10 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute - float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * + visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * visitation.visitor->getLOD().threshold; - visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier()); + visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase * + visitation.visitor->getMinimumLODThresholdMultiplier()); visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves(); int encodedOrder = visitation.visitor->visit(visitation.info); if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { @@ -1628,14 +1629,15 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) { return true; } - return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, lodBase, encodedOrder)); + return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, encodedOrder)); } bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { // save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute - float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * + visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) * visitation.visitor->getLOD().threshold; - visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier()); + visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase * + visitation.visitor->getMinimumLODThresholdMultiplier()); visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves(); int encodedOrder = visitation.visitor->visit(visitation.info); if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) { @@ -1658,7 +1660,7 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { return true; } if (encodedOrder & MetavoxelVisitor::ALL_NODES_REST) { - return defaultGuideToChildren(visitation, lodBase, encodedOrder); + return defaultGuideToChildren(visitation, encodedOrder); } bool onlyVisitDifferent = !(encodedOrder & MetavoxelVisitor::ALL_NODES); MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation(); @@ -1672,7 +1674,8 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { for (int j = 0; j < visitation.inputNodes.size(); j++) { MetavoxelNode* node = visitation.inputNodes.at(j); const AttributeValue& parentValue = visitation.info.inputValues.at(j); - bool expand = (visitation.info.size >= lodBase * parentValue.getAttribute()->getLODThresholdMultiplier()); + bool expand = (visitation.info.size >= visitation.info.lodBase * + parentValue.getAttribute()->getLODThresholdMultiplier()); MetavoxelNode* child = (node && expand) ? node->getChild(index) : NULL; nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue); @@ -1686,7 +1689,7 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) { } for (int j = 0; j < visitation.outputNodes.size(); j++) { MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = (node && (visitation.info.size >= lodBase * + MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase * visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL; nextVisitation.outputNodes[j] = child; } @@ -1911,6 +1914,12 @@ MetavoxelVisitation::MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitation::MetavoxelVisitation() { } +bool MetavoxelVisitation::isInputLeaf(int index) const { + MetavoxelNode* node = inputNodes.at(index); + return !node || node->isLeaf() || info.size < info.lodBase * + info.inputValues.at(index).getAttribute()->getLODThresholdMultiplier(); +} + bool MetavoxelVisitation::allInputNodesLeaves() const { foreach (MetavoxelNode* node, inputNodes) { if (node && !node->isLeaf()) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 8a240b9d7b..3efb0fc8f2 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -278,6 +278,7 @@ public: float size; ///< the size of the voxel in all dimensions QVector inputValues; QVector outputValues; + float lodBase; bool isLODLeaf; bool isLeaf; @@ -537,6 +538,7 @@ public: MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize); MetavoxelVisitation(); + bool isInputLeaf(int index) const; bool allInputNodesLeaves() const; AttributeValue getInheritedOutputValue(int index) const; }; From 2314ab36243321da82ca11c97e2ad8de31a82323 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 30 Oct 2014 18:05:40 -0700 Subject: [PATCH 14/50] Removed unused variable, fixed painting materials on heightfields with greater resolution. --- libraries/metavoxels/src/MetavoxelMessages.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 4c6611f4bd..c9c2d32b7e 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -445,7 +445,8 @@ PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const g _material(material), _color(color) { - glm::vec3 extents(_radius, _radius, _radius); + const float LARGE_EXTENT = 100000.0f; + glm::vec3 extents(_radius, LARGE_EXTENT, _radius); _bounds = Box(_position - extents, _position + extents); } @@ -520,7 +521,6 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) { uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX; float squaredRadius = scaledRadius * scaledRadius; bool changed = false; - QHash counts; for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) { uchar* dest = lineDest; for (float x = startX; x <= endX; x += 1.0f, dest++) { From f84030ce3fb26cad0fd441c3d73379b213b94d92 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 30 Oct 2014 19:16:29 -0700 Subject: [PATCH 15/50] Fix pan and orbit camera controls in inspect.js after API change --- examples/inspect.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/examples/inspect.js b/examples/inspect.js index b9ed5a3f00..81beea7ee9 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -17,13 +17,17 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var PI = 3.14 // No need for something more precise +var PI = Math.PI; +var RAD_TO_DEG = 180.0 / PI; var AZIMUTH_RATE = 90.0; var ALTITUDE_RATE = 200.0; var RADIUS_RATE = 1.0 / 100.0; var PAN_RATE = 50.0; +var Y_AXIS = { x: 0, y: 1, z: 0 }; +var X_AXIS = { x: 1, y: 0, z: 0 }; + var alt = false; var shift = false; var control = false; @@ -53,6 +57,18 @@ var avatarPosition; var avatarOrientation; +function orientationOf(vector) { + var direction, + yaw, + pitch; + + direction = Vec3.normalize(vector); + yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS); + pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS); + return Quat.multiply(yaw, pitch); +} + + function handleRadialMode(dx, dy) { azimuth += dx / AZIMUTH_RATE; radius += radius * dy * RADIUS_RATE; @@ -65,6 +81,7 @@ function handleRadialMode(dx, dy) { z: (Math.cos(altitude) * Math.sin(azimuth)) * radius }; position = Vec3.sum(center, vector); Camera.setPosition(position); + Camera.setOrientation(orientationOf(vector)); } function handleOrbitMode(dx, dy) { @@ -82,6 +99,7 @@ function handleOrbitMode(dx, dy) { z:(Math.cos(altitude) * Math.sin(azimuth)) * radius }; position = Vec3.sum(center, vector); Camera.setPosition(position); + Camera.setOrientation(orientationOf(vector)); } @@ -96,7 +114,7 @@ function handlePanMode(dx, dy) { position = Vec3.sum(position, dv); Camera.setPosition(position); - Camera.keepLookingAt(center); + Camera.setOrientation(orientationOf(vector)); } function saveCameraState() { @@ -107,7 +125,6 @@ function saveCameraState() { } function restoreCameraState() { - Camera.stopLooking(); Camera.setMode(oldMode); } @@ -245,7 +262,6 @@ function mousePressEvent(event) { azimuth = Math.atan2(vector.z, vector.x); altitude = Math.asin(vector.y / Vec3.length(vector)); - Camera.keepLookingAt(center); print(string); isActive = true; } From 29eeeb33841d0e84522bd5254688262559f05dc3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 31 Oct 2014 09:24:50 -0700 Subject: [PATCH 16/50] Fix log file missing end of lines --- interface/src/FileLogger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp index 4808842036..505cb6a029 100644 --- a/interface/src/FileLogger.cpp +++ b/interface/src/FileLogger.cpp @@ -41,7 +41,7 @@ void FileLogger::addMessage(QString message) { QFile file(_fileName); if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { QTextStream out(&file); - out << message; + out << message << "\n"; } } From ccc5e0d6cafeb0dd5bb68a1ae94baade27725b37 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 31 Oct 2014 10:06:43 -0700 Subject: [PATCH 17/50] Increase Interface's disk cache from default 50MB to 10GB --- interface/src/Application.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0752f9fa9e..2a8b15ad39 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -109,6 +109,9 @@ static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged c const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff // in the idle loop? (60 FPS is default) + +const unsigned MAXIMUM_CACHE_SIZE = 10737418240; // 10GB + static QTimer* idleTimer = NULL; const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml"; @@ -347,9 +350,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkDiskCache* cache = new QNetworkDiskCache(); + cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); networkAccessManager.setCache(cache); From 277da5a24f703cd730a71a9d980a5c1b61e42d8a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 31 Oct 2014 10:23:45 -0700 Subject: [PATCH 18/50] first cut at wiring up new entity specific UI/UX events --- interface/src/Application.cpp | 12 ++ interface/src/entities/EntityTreeRenderer.cpp | 132 ++++++++++++++++++ interface/src/entities/EntityTreeRenderer.h | 29 ++++ libraries/entities/src/EntityItemID.h | 7 + .../entities/src/EntityScriptingInterface.h | 13 ++ 5 files changed, 193 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0752f9fa9e..f3a6899174 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1248,6 +1248,8 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { if (_lastMouseMoveWasSimulated) { showMouse = false; } + + _entities.mouseMoveEvent(event, deviceID); _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts @@ -1269,6 +1271,9 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { } void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { + + _entities.mousePressEvent(event, deviceID); + _controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it @@ -1306,6 +1311,9 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { } void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { + + _entities.mouseReleaseEvent(event, deviceID); + _controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it @@ -1940,6 +1948,10 @@ void Application::init() { connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithEntity, ScriptEngine::getEntityScriptingInterface(), &EntityScriptingInterface::entityCollisionWithEntity); + // connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing + // of events related clicking, hovering over, and entering entities + _entities.connectSignalsToSlots(ScriptEngine::getEntityScriptingInterface()); + _entityClipboardRenderer.init(); _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index e6b640011d..7335592fa3 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -24,6 +25,8 @@ #include "Menu.h" #include "EntityTreeRenderer.h" +#include "devices/OculusManager.h" + #include "RenderableBoxEntityItem.h" #include "RenderableLightEntityItem.h" #include "RenderableModelEntityItem.h" @@ -42,6 +45,9 @@ EntityTreeRenderer::EntityTreeRenderer() : REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory) + + _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID + _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID } EntityTreeRenderer::~EntityTreeRenderer() { @@ -382,4 +388,130 @@ void EntityTreeRenderer::deleteReleasedModels() { } } +PickRay EntityTreeRenderer::computePickRay(float x, float y) { + float screenWidth = Application::getInstance()->getGLWidget()->width(); + float screenHeight = Application::getInstance()->getGLWidget()->height(); + PickRay result; + if (OculusManager::isConnected()) { + Camera* camera = Application::getInstance()->getCamera(); + result.origin = camera->getPosition(); + Application::getInstance()->getApplicationOverlay().computeOculusPickRay(x / screenWidth, y / screenHeight, result.direction); + } else { + ViewFrustum* viewFrustum = Application::getInstance()->getViewFrustum(); + viewFrustum->computePickRay(x / screenWidth, y / screenHeight, result.origin, result.direction); + } + return result; +} + + +RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType) { + RayToEntityIntersectionResult result; + if (_tree) { + EntityTree* entityTree = static_cast(_tree); + + OctreeElement* element; + EntityItem* intersectedEntity = NULL; + result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, + (void**)&intersectedEntity, lockType, &result.accurate); + if (result.intersects && intersectedEntity) { + result.entityID = intersectedEntity->getEntityItemID(); + result.properties = intersectedEntity->getProperties(); + result.intersection = ray.origin + (ray.direction * result.distance); + } + } + return result; +} + +void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) { + connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); + connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); + connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); + + connect(this, &EntityTreeRenderer::clickDownOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity); + connect(this, &EntityTreeRenderer::holdingClickOnEntity, entityScriptingInterface, &EntityScriptingInterface::holdingClickOnEntity); + connect(this, &EntityTreeRenderer::clickReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickReleaseOnEntity); + + connect(this, &EntityTreeRenderer::hoverEnterEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity); + connect(this, &EntityTreeRenderer::hoverOverEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); + connect(this, &EntityTreeRenderer::hoverLeaveEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); +} + +void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { + PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); + PickRay ray = computePickRay(event->x(), event->y()); + RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock); + if (rayPickResult.intersects) { + //qDebug() << "mousePressEvent over entity:" << rayPickResult.entityID; + emit mousePressOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + + _currentClickingOnEntityID = rayPickResult.entityID; + emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + } +} + +void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { + PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent"); + PickRay ray = computePickRay(event->x(), event->y()); + RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock); + if (rayPickResult.intersects) { + //qDebug() << "mouseReleaseEvent over entity:" << rayPickResult.entityID; + emit mouseReleaseOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + } + + // Even if we're no longer intersecting with an entity, if we started clicking on it, and now + // we're releasing the button, then this is considered a clickOn event + if (!_currentClickingOnEntityID.isInvalidID()) { + emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + } + + // makes it the unknown ID, we just released so we can't be clicking on anything + _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); +} + +void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { + PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent"); + PickRay ray = computePickRay(event->x(), event->y()); + RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock); + if (rayPickResult.intersects) { + //qDebug() << "mouseMoveEvent over entity:" << rayPickResult.entityID; + emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + + // handle the hover logic... + + // if we were previously hovering over an entity, and this new entity is not the same as our previous entity + // then we need to send the hover leave. + if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) { + emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); + } + + // If the new hover entity does not match the previous hover entity then we are entering the new one + // this is true if the _currentHoverOverEntityID is known or unknown + if (rayPickResult.entityID != _currentHoverOverEntityID) { + emit hoverEnterEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + } + + // and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and + // we should send our hover over event + emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + + // remember what we're hovering over + _currentHoverOverEntityID = rayPickResult.entityID; + + } else { + // handle the hover logic... + // if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to + // send the hover leave for our previous entity + if (!_currentHoverOverEntityID.isInvalidID()) { + emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); + _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID + } + } + + // Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have + // not yet released the hold then this is still considered a holdingClickOnEntity event + if (!_currentClickingOnEntityID.isInvalidID()) { + emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + } +} + diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 09d931541d..054c947272 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -16,6 +16,7 @@ #include #include +#include // for RayToEntityIntersectionResult #include #include #include @@ -75,9 +76,37 @@ public: void releaseModel(Model* model); void deleteReleasedModels(); + + // event handles which may generate entity related events + void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID); + void mousePressEvent(QMouseEvent* event, unsigned int deviceID); + void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID); + + /// connect our signals to anEntityScriptingInterface for firing of events related clicking, + /// hovering over, and entering entities + void connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface); + +signals: + void mousePressOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void mouseMoveOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void mouseReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + + void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void clickReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + + void hoverEnterEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event); + private: QList _releasedModels; void renderProxies(const EntityItem* entity, RenderArgs* args); + PickRay computePickRay(float x, float y); + RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType); + + EntityItemID _currentHoverOverEntityID; + EntityItemID _currentClickingOnEntityID; }; #endif // hifi_EntityTreeRenderer_h diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 7a3f822c7c..919037f7d4 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -49,7 +49,10 @@ public: EntityItemID convertToKnownIDVersion() const; EntityItemID convertToCreatorTokenVersion() const; + bool isInvalidID() const { return id == UNKNOWN_ENTITY_ID && creatorTokenID == UNKNOWN_ENTITY_TOKEN && isKnownID == false; } + // these methods allow you to create models, and later edit them. + static EntityItemID createInvalidEntityID() { return EntityItemID(UNKNOWN_ENTITY_ID, UNKNOWN_ENTITY_TOKEN, false); } static EntityItemID getIDfromCreatorTokenID(uint32_t creatorTokenID); static uint32_t getNextCreatorTokenID(); static void handleAddEntityResponse(const QByteArray& packet); @@ -74,6 +77,10 @@ inline bool operator==(const EntityItemID& a, const EntityItemID& b) { return a.id == b.id; } +inline bool operator!=(const EntityItemID& a, const EntityItemID& b) { + return !(a == b); +} + inline uint qHash(const EntityItemID& a, uint seed) { QUuid temp; if (a.id == UNKNOWN_ENTITY_ID) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 1497b2966e..904940c0bc 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -25,6 +25,7 @@ class EntityTree; +class MouseEvent; class RayToEntityIntersectionResult { @@ -101,6 +102,18 @@ signals: void entityCollisionWithVoxel(const EntityItemID& entityID, const VoxelDetail& voxel, const CollisionInfo& collision); void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const CollisionInfo& collision); + void mousePressOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void mouseMoveOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void mouseReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + + void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void clickReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + + void hoverEnterEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event); + private: void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); From a5088eece6649ec26cd52bc2148757b6270acdab Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 31 Oct 2014 14:03:30 -0700 Subject: [PATCH 19/50] first cut at running scripts from entities --- examples/libraries/entityPropertyDialogBox.js | 4 + interface/src/Application.cpp | 66 +++++----- interface/src/Application.h | 2 + interface/src/entities/EntityTreeRenderer.cpp | 115 +++++++++++++++++- interface/src/entities/EntityTreeRenderer.h | 10 +- .../entities/src/EntityScriptingInterface.cpp | 3 +- .../entities/src/EntityScriptingInterface.h | 1 + 7 files changed, 167 insertions(+), 34 deletions(-) diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index 2c4bf5329e..01e0c76e0d 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -140,6 +140,9 @@ EntityPropertyDialogBox = (function () { array.push({ label: "Visible:", value: properties.visible }); index++; + + array.push({ label: "Script:", value: properties.script }); + index++; if (properties.type == "Box" || properties.type == "Sphere") { array.push({ label: "Color:", type: "header" }); @@ -282,6 +285,7 @@ EntityPropertyDialogBox = (function () { properties.lifetime = array[index++].value; properties.visible = array[index++].value; + properties.script = array[index++].value; if (properties.type == "Box" || properties.type == "Sphere") { index++; // skip header diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f3a6899174..a215905362 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -148,7 +148,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _voxelImporter(), _importSucceded(false), _sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard), - _entityClipboardRenderer(), + _entities(true), + _entityCollisionSystem(), + _entityClipboardRenderer(false), _entityClipboard(), _wantToKillLocalVoxels(false), _viewFrustum(), @@ -3831,35 +3833,7 @@ void joystickFromScriptValue(const QScriptValue &object, Joystick* &out) { out = qobject_cast(object.toQObject()); } -ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, - bool loadScriptFromEditor, bool activateMainWindow) { - QUrl scriptUrl(scriptFilename); - const QString& scriptURLString = scriptUrl.toString(); - if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor - && !_scriptEnginesHash[scriptURLString]->isFinished()) { - - return _scriptEnginesHash[scriptURLString]; - } - - ScriptEngine* scriptEngine; - if (scriptFilename.isNull()) { - scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); - } else { - // start the script on a new thread... - scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - - if (!scriptEngine->hasScript()) { - qDebug() << "Application::loadScript(), script failed to load..."; - QMessageBox::warning(getWindow(), "Error Loading Script", scriptURLString + " failed to load."); - return NULL; - } - - _scriptEnginesHash.insertMulti(scriptURLString, scriptEngine); - _runningScriptsWidget->setRunningScripts(getRunningScripts()); - UserActivityLogger::getInstance().loadedScript(scriptURLString); - } - scriptEngine->setUserLoaded(isUserLoaded); - +void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) { // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so // we can use the same ones from the application. scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender); @@ -3941,6 +3915,38 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser // Starts an event loop, and emits workerThread->started() workerThread->start(); +} + +ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, + bool loadScriptFromEditor, bool activateMainWindow) { + QUrl scriptUrl(scriptFilename); + const QString& scriptURLString = scriptUrl.toString(); + if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor + && !_scriptEnginesHash[scriptURLString]->isFinished()) { + + return _scriptEnginesHash[scriptURLString]; + } + + ScriptEngine* scriptEngine; + if (scriptFilename.isNull()) { + scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); + } else { + // start the script on a new thread... + scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); + + if (!scriptEngine->hasScript()) { + qDebug() << "Application::loadScript(), script failed to load..."; + QMessageBox::warning(getWindow(), "Error Loading Script", scriptURLString + " failed to load."); + return NULL; + } + + _scriptEnginesHash.insertMulti(scriptURLString, scriptEngine); + _runningScriptsWidget->setRunningScripts(getRunningScripts()); + UserActivityLogger::getInstance().loadedScript(scriptURLString); + } + scriptEngine->setUserLoaded(isUserLoaded); + + registerScriptEngineWithApplicationServices(scriptEngine); // restore the main window's active state if (activateMainWindow && !loadScriptFromEditor) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 3d2fab07bf..58a7a58bf3 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -305,6 +305,8 @@ public: float getRenderResolutionScale() const { return _renderResolutionScale; } + void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine); + signals: /// Fired when we're simulating; allows external parties to hook in. diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 7335592fa3..9ee984354b 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include "InterfaceConfig.h" @@ -39,8 +41,10 @@ QThread* EntityTreeRenderer::getMainThread() { -EntityTreeRenderer::EntityTreeRenderer() : - OctreeRenderer() { +EntityTreeRenderer::EntityTreeRenderer(bool wantScripts) : + OctreeRenderer(), + _wantScripts(wantScripts), + _entitiesScriptEngine(NULL) { REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory) @@ -51,6 +55,7 @@ EntityTreeRenderer::EntityTreeRenderer() : } EntityTreeRenderer::~EntityTreeRenderer() { + // do we need to delete the _entitiesScriptEngine?? or is it deleted by default } void EntityTreeRenderer::clear() { @@ -60,6 +65,51 @@ void EntityTreeRenderer::clear() { void EntityTreeRenderer::init() { OctreeRenderer::init(); static_cast(_tree)->setFBXService(this); + + if (_wantScripts) { + _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities", + Application::getInstance()->getControllerScriptingInterface()); + Application::getInstance()->registerScriptEngineWithApplicationServices(_entitiesScriptEngine); + } +} + +QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) { + EntityItem* entity = static_cast(_tree)->findEntityByEntityItemID(entityItemID); + return loadEntityScript(entity); +} + + +QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { + if (!entity) { + return QScriptValue(); // no entity... + } + if (_entityScripts.contains(entity->getEntityItemID())) { + return _entityScripts[entity->getEntityItemID()]; // already loaded + } + if (entity->getScript().isEmpty()) { + return QScriptValue(); // no script + } + + if (QScriptEngine::checkSyntax(entity->getScript()).state() != QScriptSyntaxCheckResult::Valid) { + qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entity->getEntityItemID(); + qDebug() << " INVALID SYNTAX"; + qDebug() << " SCRIPT:" << entity->getScript(); + return QScriptValue(); // invalid script + } + + QScriptValue entityScriptConstructor = _entitiesScriptEngine->evaluate(entity->getScript()); + + if (!entityScriptConstructor.isFunction()) { + qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entity->getEntityItemID(); + qDebug() << " NOT CONSTRUCTOR"; + qDebug() << " SCRIPT:" << entity->getScript(); + return QScriptValue(); // invalid script + } + + QScriptValue entityScriptObject = entityScriptConstructor.construct(); + _entityScripts[entity->getEntityItemID()] = entityScriptObject; + + return entityScriptObject; // newly constructed } void EntityTreeRenderer::setTree(Octree* newTree) { @@ -417,6 +467,7 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons result.entityID = intersectedEntity->getEntityItemID(); result.properties = intersectedEntity->getProperties(); result.intersection = ray.origin + (ray.direction * result.distance); + result.entity = intersectedEntity; } } return result; @@ -443,9 +494,17 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device if (rayPickResult.intersects) { //qDebug() << "mousePressEvent over entity:" << rayPickResult.entityID; emit mousePressOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + + QScriptValue entityScript = loadEntityScript(rayPickResult.entity); + if (entityScript.property("mousePressOnEntity").isValid()) { + entityScript.property("mousePressOnEntity").call(); + } _currentClickingOnEntityID = rayPickResult.entityID; emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + if (entityScript.property("clickDownOnEntity").isValid()) { + entityScript.property("clickDownOnEntity").call(); + } } } @@ -456,12 +515,22 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi if (rayPickResult.intersects) { //qDebug() << "mouseReleaseEvent over entity:" << rayPickResult.entityID; emit mouseReleaseOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + + QScriptValue entityScript = loadEntityScript(rayPickResult.entity); + if (entityScript.property("mouseReleaseOnEntity").isValid()) { + entityScript.property("mouseReleaseOnEntity").call(); + } } // Even if we're no longer intersecting with an entity, if we started clicking on it, and now // we're releasing the button, then this is considered a clickOn event if (!_currentClickingOnEntityID.isInvalidID()) { emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + + QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); + if (currentClickingEntity.property("clickReleaseOnEntity").isValid()) { + currentClickingEntity.property("clickReleaseOnEntity").call(); + } } // makes it the unknown ID, we just released so we can't be clicking on anything @@ -469,12 +538,33 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi } void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { + + // experimental... + /* + qDebug() << "TESTING - "; + qDebug() << " _experimentalScriptValue.isValid():" << _experimentalScriptValue.isValid(); + qDebug() << " _experimentalConstructed.isValid():" << _experimentalConstructed.isValid(); + qDebug() << " _experimentalConstructed.property(MethodA).isValid():" << _experimentalConstructed.property("MethodA").isValid(); + _experimentalConstructed.property("MethodA").call(); + */ + + PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent"); PickRay ray = computePickRay(event->x(), event->y()); RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock); if (rayPickResult.intersects) { + + // load the entity script if needed... + QScriptValue entityScript = loadEntityScript(rayPickResult.entity); + if (entityScript.property("mouseMoveEvent").isValid()) { + entityScript.property("mouseMoveEvent").call(); + } + //qDebug() << "mouseMoveEvent over entity:" << rayPickResult.entityID; emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + if (entityScript.property("mouseMoveOnEntity").isValid()) { + entityScript.property("mouseMoveOnEntity").call(); + } // handle the hover logic... @@ -482,17 +572,28 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI // then we need to send the hover leave. if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) { emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); + + QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); + if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { + currentHoverEntity.property("hoverLeaveEntity").call(); + } } // If the new hover entity does not match the previous hover entity then we are entering the new one // this is true if the _currentHoverOverEntityID is known or unknown if (rayPickResult.entityID != _currentHoverOverEntityID) { emit hoverEnterEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + if (entityScript.property("hoverEnterEntity").isValid()) { + entityScript.property("hoverEnterEntity").call(); + } } // and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and // we should send our hover over event emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + if (entityScript.property("hoverOverEntity").isValid()) { + entityScript.property("hoverOverEntity").call(); + } // remember what we're hovering over _currentHoverOverEntityID = rayPickResult.entityID; @@ -503,6 +604,12 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI // send the hover leave for our previous entity if (!_currentHoverOverEntityID.isInvalidID()) { emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); + + QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); + if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { + currentHoverEntity.property("hoverLeaveEntity").call(); + } + _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID } } @@ -511,6 +618,10 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI // not yet released the hold then this is still considered a holdingClickOnEntity event if (!_currentClickingOnEntityID.isInvalidID()) { emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); + if (currentClickingEntity.property("holdingClickOnEntity").isValid()) { + currentClickingEntity.property("holdingClickOnEntity").call(); + } } } diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 054c947272..4648db0545 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -31,7 +31,7 @@ class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService { Q_OBJECT public: - EntityTreeRenderer(); + EntityTreeRenderer(bool wantScripts); virtual ~EntityTreeRenderer(); virtual Octree* createTree() { return new EntityTree(true); } @@ -107,6 +107,14 @@ private: EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; + + bool _wantScripts; + ScriptEngine* _entitiesScriptEngine; + + QScriptValue loadEntityScript(EntityItem* entity); + QScriptValue loadEntityScript(const EntityItemID& entityItemID); + + QHash _entityScripts; }; #endif // hifi_EntityTreeRenderer_h diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 8113168655..1658764c71 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -217,7 +217,8 @@ RayToEntityIntersectionResult::RayToEntityIntersectionResult() : entityID(), properties(), distance(0), - face() + face(), + entity(NULL) { } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 904940c0bc..1152ccbd03 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -38,6 +38,7 @@ public: float distance; BoxFace face; glm::vec3 intersection; + EntityItem* entity; }; Q_DECLARE_METATYPE(RayToEntityIntersectionResult) From e7722dc6921f6146fcb3993cd23ddb4c95555e72 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 31 Oct 2014 15:20:16 -0700 Subject: [PATCH 20/50] pass args to object scripts for mouse events --- interface/src/entities/EntityTreeRenderer.cpp | 40 ++++++++++--------- interface/src/entities/EntityTreeRenderer.h | 1 + libraries/entities/src/EntityItemID.cpp | 4 ++ libraries/entities/src/EntityItemID.h | 9 ++++- libraries/script-engine/src/MouseEvent.h | 2 + 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 9ee984354b..46a56e63fe 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -537,33 +537,30 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); } +QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) { + QScriptValueList args; + args << entityID.toScriptValue(_entitiesScriptEngine); + args << MouseEvent(*event, deviceID).toScriptValue(_entitiesScriptEngine); + return args; +} + void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { - - // experimental... - /* - qDebug() << "TESTING - "; - qDebug() << " _experimentalScriptValue.isValid():" << _experimentalScriptValue.isValid(); - qDebug() << " _experimentalConstructed.isValid():" << _experimentalConstructed.isValid(); - qDebug() << " _experimentalConstructed.property(MethodA).isValid():" << _experimentalConstructed.property("MethodA").isValid(); - _experimentalConstructed.property("MethodA").call(); - */ - - PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent"); PickRay ray = computePickRay(event->x(), event->y()); RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock); if (rayPickResult.intersects) { + QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); // load the entity script if needed... QScriptValue entityScript = loadEntityScript(rayPickResult.entity); if (entityScript.property("mouseMoveEvent").isValid()) { - entityScript.property("mouseMoveEvent").call(); + entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs); } //qDebug() << "mouseMoveEvent over entity:" << rayPickResult.entityID; emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); if (entityScript.property("mouseMoveOnEntity").isValid()) { - entityScript.property("mouseMoveOnEntity").call(); + entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs); } // handle the hover logic... @@ -573,9 +570,11 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) { emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); + QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID); + QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { - currentHoverEntity.property("hoverLeaveEntity").call(); + currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs); } } @@ -584,7 +583,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI if (rayPickResult.entityID != _currentHoverOverEntityID) { emit hoverEnterEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); if (entityScript.property("hoverEnterEntity").isValid()) { - entityScript.property("hoverEnterEntity").call(); + entityScript.property("hoverEnterEntity").call(entityScript, entityScriptArgs); } } @@ -592,7 +591,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI // we should send our hover over event emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); if (entityScript.property("hoverOverEntity").isValid()) { - entityScript.property("hoverOverEntity").call(); + entityScript.property("hoverOverEntity").call(entityScript, entityScriptArgs); } // remember what we're hovering over @@ -605,9 +604,11 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI if (!_currentHoverOverEntityID.isInvalidID()) { emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID)); + QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID); + QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { - currentHoverEntity.property("hoverLeaveEntity").call(); + currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs); } _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID @@ -618,9 +619,12 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI // not yet released the hold then this is still considered a holdingClickOnEntity event if (!_currentClickingOnEntityID.isInvalidID()) { emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + + QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID); + QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); if (currentClickingEntity.property("holdingClickOnEntity").isValid()) { - currentClickingEntity.property("holdingClickOnEntity").call(); + currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs); } } } diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 4648db0545..a9a5b96ed9 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -113,6 +113,7 @@ private: QScriptValue loadEntityScript(EntityItem* entity); QScriptValue loadEntityScript(const EntityItemID& entityItemID); + QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID); QHash _entityScripts; }; diff --git a/libraries/entities/src/EntityItemID.cpp b/libraries/entities/src/EntityItemID.cpp index c730e44322..aaf6e33128 100644 --- a/libraries/entities/src/EntityItemID.cpp +++ b/libraries/entities/src/EntityItemID.cpp @@ -124,6 +124,10 @@ void EntityItemID::handleAddEntityResponse(const QByteArray& packet) { _tokenIDsToIDs[creatorTokenID] = entityID; } +QScriptValue EntityItemID::toScriptValue(QScriptEngine* engine) const { + return EntityItemIDtoScriptValue(engine, *this); +} + QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& id) { QScriptValue obj = engine->newObject(); obj.setProperty("id", id.id.toString()); diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 919037f7d4..1ac8a67fcd 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -57,6 +57,8 @@ public: static uint32_t getNextCreatorTokenID(); static void handleAddEntityResponse(const QByteArray& packet); static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead); + + QScriptValue toScriptValue(QScriptEngine* engine) const; private: friend class EntityTree; @@ -71,6 +73,12 @@ inline bool operator<(const EntityItemID& a, const EntityItemID& b) { } inline bool operator==(const EntityItemID& a, const EntityItemID& b) { + if (a.isInvalidID() && b.isInvalidID()) { + return true; + } + if (a.isInvalidID() != b.isInvalidID()) { + return false; + } if (a.id == UNKNOWN_ENTITY_ID || b.id == UNKNOWN_ENTITY_ID) { return a.creatorTokenID == b.creatorTokenID; } @@ -101,5 +109,4 @@ Q_DECLARE_METATYPE(QVector); QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& properties); void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties); - #endif // hifi_EntityItemID_h diff --git a/libraries/script-engine/src/MouseEvent.h b/libraries/script-engine/src/MouseEvent.h index 7555f2ea5a..1936e6b58e 100644 --- a/libraries/script-engine/src/MouseEvent.h +++ b/libraries/script-engine/src/MouseEvent.h @@ -21,6 +21,8 @@ public: static QScriptValue toScriptValue(QScriptEngine* engine, const MouseEvent& event); static void fromScriptValue(const QScriptValue& object, MouseEvent& event); + + QScriptValue toScriptValue(QScriptEngine* engine) const { return MouseEvent::toScriptValue(engine, *this); } int x; int y; From e8c694f02e954581fca76a0050b5fb3b171b69f5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 31 Oct 2014 16:38:51 -0700 Subject: [PATCH 21/50] args for more methods --- interface/src/entities/EntityTreeRenderer.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 46a56e63fe..c960f1057a 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -495,15 +495,16 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device //qDebug() << "mousePressEvent over entity:" << rayPickResult.entityID; emit mousePressOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); QScriptValue entityScript = loadEntityScript(rayPickResult.entity); if (entityScript.property("mousePressOnEntity").isValid()) { - entityScript.property("mousePressOnEntity").call(); + entityScript.property("mousePressOnEntity").call(entityScript, entityScriptArgs); } _currentClickingOnEntityID = rayPickResult.entityID; emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); if (entityScript.property("clickDownOnEntity").isValid()) { - entityScript.property("clickDownOnEntity").call(); + entityScript.property("clickDownOnEntity").call(entityScript, entityScriptArgs); } } } @@ -516,9 +517,10 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi //qDebug() << "mouseReleaseEvent over entity:" << rayPickResult.entityID; emit mouseReleaseOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); + QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); QScriptValue entityScript = loadEntityScript(rayPickResult.entity); if (entityScript.property("mouseReleaseOnEntity").isValid()) { - entityScript.property("mouseReleaseOnEntity").call(); + entityScript.property("mouseReleaseOnEntity").call(entityScript, entityScriptArgs); } } @@ -527,9 +529,10 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi if (!_currentClickingOnEntityID.isInvalidID()) { emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); + QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID); QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); if (currentClickingEntity.property("clickReleaseOnEntity").isValid()) { - currentClickingEntity.property("clickReleaseOnEntity").call(); + currentClickingEntity.property("clickReleaseOnEntity").call(currentClickingEntity, currentClickingEntityArgs); } } From 87a5423b35f42cbd87c75ea988291903e359f610 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Nov 2014 10:57:12 -0700 Subject: [PATCH 22/50] support changing scripts on the fly and reloading them accordningly --- interface/src/entities/EntityTreeRenderer.cpp | 32 +++++++++++++------ interface/src/entities/EntityTreeRenderer.h | 8 ++++- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index c960f1057a..fe2db389f6 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -83,8 +83,19 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { if (!entity) { return QScriptValue(); // no entity... } - if (_entityScripts.contains(entity->getEntityItemID())) { - return _entityScripts[entity->getEntityItemID()]; // already loaded + + EntityItemID entityID = entity->getEntityItemID(); + if (_entityScripts.contains(entityID)) { + EntityScriptDetails details = _entityScripts[entityID]; + + // check to make sure our script text hasn't changed on us since we last loaded it + if (details.scriptText == entity->getScript()) { + return details.scriptObject; // previously loaded + } + + // if we got here, then we previously loaded a script, but the entity's script value + // has changed and so we need to reload it. + _entityScripts.remove(entityID); } if (entity->getScript().isEmpty()) { return QScriptValue(); // no script @@ -107,7 +118,8 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { } QScriptValue entityScriptObject = entityScriptConstructor.construct(); - _entityScripts[entity->getEntityItemID()] = entityScriptObject; + EntityScriptDetails newDetails = { entity->getScript(), entityScriptObject }; + _entityScripts[entityID] = newDetails; return entityScriptObject; // newly constructed } @@ -487,6 +499,13 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS connect(this, &EntityTreeRenderer::hoverLeaveEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); } +QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) { + QScriptValueList args; + args << entityID.toScriptValue(_entitiesScriptEngine); + args << MouseEvent(*event, deviceID).toScriptValue(_entitiesScriptEngine); + return args; +} + void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); PickRay ray = computePickRay(event->x(), event->y()); @@ -540,13 +559,6 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); } -QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) { - QScriptValueList args; - args << entityID.toScriptValue(_entitiesScriptEngine); - args << MouseEvent(*event, deviceID).toScriptValue(_entitiesScriptEngine); - return args; -} - void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent"); PickRay ray = computePickRay(event->x(), event->y()); diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index a9a5b96ed9..87d1ed0f8a 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -27,6 +27,12 @@ #include "renderer/Model.h" +class EntityScriptDetails { +public: + QString scriptText; + QScriptValue scriptObject; +}; + // Generic client side Octree renderer class. class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService { Q_OBJECT @@ -115,7 +121,7 @@ private: QScriptValue loadEntityScript(const EntityItemID& entityItemID); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID); - QHash _entityScripts; + QHash _entityScripts; }; #endif // hifi_EntityTreeRenderer_h From 4db59866257fda13c8bc384ccccd07b51e5c6083 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Nov 2014 11:40:06 -0700 Subject: [PATCH 23/50] support for url or text based scripts --- interface/src/entities/EntityTreeRenderer.cpp | 62 +++++++++++++++++-- interface/src/entities/EntityTreeRenderer.h | 1 + 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index fe2db389f6..df2c51104b 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -25,6 +25,7 @@ #include "Menu.h" +#include "NetworkAccessManager.h" #include "EntityTreeRenderer.h" #include "devices/OculusManager.h" @@ -79,6 +80,53 @@ QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItem } +QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText) { + QUrl url(scriptMaybeURLorText); + + // If the url is not valid, this must be script text... + if (!url.isValid()) { + return scriptMaybeURLorText; + } + + QString scriptContents; // assume empty + + // if the scheme length is one or lower, maybe they typed in a file, let's try + const int WINDOWS_DRIVE_LETTER_SIZE = 1; + if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { + url = QUrl::fromLocalFile(scriptMaybeURLorText); + } + + // ok, let's see if it's valid... and if so, load it + if (url.isValid()) { + if (url.scheme() == "file") { + QString fileName = url.toLocalFile(); + QFile scriptFile(fileName); + if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { + qDebug() << "Loading file:" << fileName; + QTextStream in(&scriptFile); + scriptContents = in.readAll(); + } else { + qDebug() << "ERROR Loading file:" << fileName; + } + } else { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); + qDebug() << "Downloading script at" << url; + QEventLoop loop; + QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) { + scriptContents = reply->readAll(); + } else { + qDebug() << "ERROR Loading file:" << url.toString(); + } + } + } + + return scriptContents; +} + + QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { if (!entity) { return QScriptValue(); // no entity... @@ -101,19 +149,21 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { return QScriptValue(); // no script } - if (QScriptEngine::checkSyntax(entity->getScript()).state() != QScriptSyntaxCheckResult::Valid) { - qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entity->getEntityItemID(); + QString scriptContents = loadScriptContents(entity->getScript()); + + if (QScriptEngine::checkSyntax(scriptContents).state() != QScriptSyntaxCheckResult::Valid) { + qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID; qDebug() << " INVALID SYNTAX"; - qDebug() << " SCRIPT:" << entity->getScript(); + qDebug() << " SCRIPT:" << scriptContents; return QScriptValue(); // invalid script } - QScriptValue entityScriptConstructor = _entitiesScriptEngine->evaluate(entity->getScript()); + QScriptValue entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents); if (!entityScriptConstructor.isFunction()) { - qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entity->getEntityItemID(); + qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID; qDebug() << " NOT CONSTRUCTOR"; - qDebug() << " SCRIPT:" << entity->getScript(); + qDebug() << " SCRIPT:" << scriptContents; return QScriptValue(); // invalid script } diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 87d1ed0f8a..6c3f948594 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -119,6 +119,7 @@ private: QScriptValue loadEntityScript(EntityItem* entity); QScriptValue loadEntityScript(const EntityItemID& entityItemID); + QString loadScriptContents(const QString& scriptMaybeURLorText); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID); QHash _entityScripts; From 2ffe15c833822c4ec09bd1d7d6e6deb7d854ffb7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 1 Nov 2014 11:43:40 -0700 Subject: [PATCH 24/50] add example entity script for change color on hover --- examples/entityScripts/changeColorOnHover.js | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 examples/entityScripts/changeColorOnHover.js diff --git a/examples/entityScripts/changeColorOnHover.js b/examples/entityScripts/changeColorOnHover.js new file mode 100644 index 0000000000..de3f5f3784 --- /dev/null +++ b/examples/entityScripts/changeColorOnHover.js @@ -0,0 +1,38 @@ +// +// changeColorOnHover.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to a non-model entity like a box or sphere, will +// change the color of the entity when you hover over it. This script uses the JavaScript prototype/class functionality +// to construct an object that has methods for hoverEnterEntity and hoverLeaveEntity; +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function(){ + this.oldColor = {}; + this.oldColorKnown = false; + this.storeOldColor = function(entityID) { + var oldProperties = Entities.getEntityProperties(entityID); + this.oldColor = oldProperties.color; + this.oldColorKnown = true; + print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); + }; + this.hoverEnterEntity = function(entityID, mouseEvent) { + if (!this.oldColorKnown) { + this.storeOldColor(entityID); + } + Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} }); + }; + this.hoverLeaveEntity = function(entityID, mouseEvent) { + if (this.oldColorKnown) { + print("leave restoring old color... this.oldColor=" + + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); + Entities.editEntity(entityID, { color: this.oldColor }); + } + }; +}) \ No newline at end of file From 2746733687c78d150d3e498f379007ab8c58685c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 10:46:17 -0800 Subject: [PATCH 25/50] don't allow HifiSockAddr lookup to block application --- libraries/networking/src/HifiSockAddr.cpp | 33 +++++++++++++++----- libraries/networking/src/HifiSockAddr.h | 8 +++-- libraries/networking/src/LimitedNodeList.cpp | 6 ++-- libraries/networking/src/LimitedNodeList.h | 1 + 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index 97e9721356..2cb0aaad83 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -36,14 +36,15 @@ HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) { _port = otherSockAddr._port; } -HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) { - // lookup the IP by the hostname - QHostInfo hostInfo = QHostInfo::fromName(hostname); - foreach(const QHostAddress& address, hostInfo.addresses()) { - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - _address = address; - _port = hostOrderPort; - } +HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) : + _address(hostname), + _port(hostOrderPort) +{ + // if we parsed an IPv4 address out of the hostname, don't look it up + if (_address.protocol() != QAbstractSocket::IPv4Protocol) { + // sync lookup the IP by the hostname + int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo))); + qDebug() << "Looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; } } @@ -75,6 +76,22 @@ bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const { return _address == rhsSockAddr._address && _port == rhsSockAddr._port; } +void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) { + if (hostInfo.error() != QHostInfo::NoError) { + qDebug() << "Lookup failed for" << hostInfo.lookupId() << ":" << hostInfo.errorString(); + } + + foreach(const QHostAddress& address, hostInfo.addresses()) { + // just take the first IPv4 address + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + _address = address; + qDebug() << "QHostInfo lookup result for" + << hostInfo.hostName() << "with lookup ID" << hostInfo.lookupId() << "is" << address.toString(); + break; + } + } +} + QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) { debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port; return debug.space(); diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 42f815390a..50dc99f4df 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -19,9 +19,11 @@ #include #endif -#include +#include +#include -class HifiSockAddr { +class HifiSockAddr : public QObject { + Q_OBJECT public: HifiSockAddr(); HifiSockAddr(const QHostAddress& address, quint16 port); @@ -51,6 +53,8 @@ public: friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr); friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr); friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr); +private slots: + void handleLookupResult(const QHostInfo& hostInfo); private: QHostAddress _address; quint16 _port; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 5c4dc6cea2..043f0621bb 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -73,6 +73,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short _dtlsSocket(NULL), _localSockAddr(), _publicSockAddr(), + _stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT), _numCollectedPackets(0), _numCollectedBytes(0), _packetStatTimer() @@ -583,11 +584,8 @@ void LimitedNodeList::sendSTUNRequest() { QUuid randomUUID = QUuid::createUuid(); memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); - // lookup the IP for the STUN server - HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); - _nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket), - stunSockAddr.getAddress(), stunSockAddr.getPort()); + _stunSockAddr.getAddress(), _stunSockAddr.getPort()); } void LimitedNodeList::rebindNodeSocket() { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 73381d01a5..64495fbd34 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -163,6 +163,7 @@ protected: QUdpSocket* _dtlsSocket; HifiSockAddr _localSockAddr; HifiSockAddr _publicSockAddr; + HifiSockAddr _stunSockAddr; int _numCollectedPackets; int _numCollectedBytes; QElapsedTimer _packetStatTimer; From d085be6c4709c8c4ea7b0ca1217c2c11888261f0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 3 Nov 2014 11:14:04 -0800 Subject: [PATCH 26/50] Better estimate of HMD offset --- examples/leapHands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/leapHands.js b/examples/leapHands.js index c9c3af0063..437637dc3f 100644 --- a/examples/leapHands.js +++ b/examples/leapHands.js @@ -16,7 +16,7 @@ var leapHands = (function () { var isOnHMD, LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD", LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip - HMD_OFFSET = 0.100, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief + HMD_OFFSET = 0.070, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief hands, wrists, NUM_HANDS = 2, // 0 = left; 1 = right From cecf79e58096515136650cde2e2ac61cb0f91fe2 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 3 Nov 2014 11:22:23 -0800 Subject: [PATCH 27/50] Fix the problem of actual framerate target not beeing true to the value displayed in the menu, same for resolution scale. Removed the value in Application and relies exclusively on the menu state --- interface/src/Application.cpp | 91 +++++++++++++++++++++++++++-------- interface/src/Application.h | 16 +++--- interface/src/Menu.cpp | 50 ++----------------- interface/src/Menu.h | 2 - 4 files changed, 83 insertions(+), 76 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f56885d16b..36591cd5b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -107,8 +107,6 @@ static unsigned STARFIELD_SEED = 1; static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored -const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff - // in the idle loop? (60 FPS is default) static QTimer* idleTimer = NULL; const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml"; @@ -182,9 +180,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _trayIcon(new QSystemTrayIcon(_window)), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), - _renderTargetFramerate(0), - _isVSyncOn(true), - _renderResolutionScale(1.0f) + _isVSyncOn(true) { // read the ApplicationInfo.ini file for Name/Version/Domain information @@ -601,7 +597,7 @@ void Application::paintGL() { if (OculusManager::isConnected()) { _textureCache.setFrameBufferSize(OculusManager::getRenderTargetSize()); } else { - QSize fbSize = _glWidget->getDeviceSize() * _renderResolutionScale; + QSize fbSize = _glWidget->getDeviceSize() * getRenderResolutionScale(); _textureCache.setFrameBufferSize(fbSize); } @@ -1471,12 +1467,11 @@ void Application::idle() { bool showWarnings = getLogger()->extraDebugging(); PerformanceWarning warn(showWarnings, "idle()"); - // Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran + // Only run simulation code if more than the targetFramePeriod have passed since last time we ran double targetFramePeriod = 0.0; - if (_renderTargetFramerate > 0) { - targetFramePeriod = 1000.0 / _renderTargetFramerate; - } else if (_renderTargetFramerate < 0) { - targetFramePeriod = IDLE_SIMULATE_MSECS; + unsigned int targetFramerate = getRenderTargetFramerate(); + if (targetFramerate > 0) { + targetFramePeriod = 1000.0 / targetFramerate; } double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0; if (timeSinceLastUpdate > targetFramePeriod) { @@ -3176,7 +3171,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { } else { // if not rendering the billboard, the region is in device independent coordinates; must convert to device QSize size = getTextureCache()->getFrameBufferSize(); - float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * _renderResolutionScale; + float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio; glViewport(x, size.height() - y - height, width, height); glScissor(x, size.height() - y - height, width, height); @@ -4234,13 +4229,11 @@ void Application::takeSnapshot() { _snapshotShareDialog->show(); } -void Application::setRenderTargetFramerate(unsigned int framerate, bool vsyncOn) { - if (vsyncOn != _isVSyncOn) { +void Application::setVSyncEnabled(bool vsyncOn) { #if defined(Q_OS_WIN) if (wglewGetExtension("WGL_EXT_swap_control")) { wglSwapIntervalEXT(vsyncOn); int swapInterval = wglGetSwapIntervalEXT(); - _isVSyncOn = swapInterval; qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); } else { qDebug("V-Sync is FORCED ON on this system\n"); @@ -4260,11 +4253,32 @@ void Application::setRenderTargetFramerate(unsigned int framerate, bool vsyncOn) #else qDebug("V-Sync is FORCED ON on this system\n"); #endif - } - _renderTargetFramerate = framerate; } -bool Application::isVSyncEditable() { +bool Application::isVSyncOn() const { +#if defined(Q_OS_WIN) + if (wglewGetExtension("WGL_EXT_swap_control")) { + int swapInterval = wglGetSwapIntervalEXT(); + return (swapInterval > 0); + } else { + return true; + } +#elif defined(Q_OS_LINUX) + // TODO: write the poper code for linux + /* + if (glQueryExtension.... ("GLX_EXT_swap_control")) { + int swapInterval = xglGetSwapIntervalEXT(); + return (swapInterval > 0); + } else { + return true; + } + */ +#else + return true; +#endif +} + +bool Application::isVSyncEditable() const { #if defined(Q_OS_WIN) if (wglewGetExtension("WGL_EXT_swap_control")) { return true; @@ -4281,6 +4295,43 @@ bool Application::isVSyncEditable() { return false; } -void Application::setRenderResolutionScale(float scale) { - _renderResolutionScale = scale; +unsigned int Application::getRenderTargetFramerate() const { + if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateUnlimited)) { + return 0; + } + else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate60)) { + return 60; + } + else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate50)) { + return 50; + } + else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate40)) { + return 40; + } + else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate30)) { + return 30; + } + return 0; } + +float Application::getRenderResolutionScale() const { + + if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { + return 1.f; + } + else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionTwoThird)) { + return 0.666f; + } + else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionHalf)) { + return 0.5f; + } + else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionThird)) { + return 0.333f; + } + else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionQuarter)) { + return 0.25f; + } + else { + return 1.f; + } +} \ No newline at end of file diff --git a/interface/src/Application.h b/interface/src/Application.h index 3d2fab07bf..f8a97549f2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -303,7 +303,12 @@ public: bool isLookingAtMyAvatar(Avatar* avatar); - float getRenderResolutionScale() const { return _renderResolutionScale; } + float getRenderResolutionScale() const; + + unsigned int getRenderTargetFramerate() const; + bool isVSyncOn() const; + bool isVSyncEditable() const; + signals: @@ -367,12 +372,7 @@ public slots: void domainSettingsReceived(const QJsonObject& domainSettingsObject); - void setRenderTargetFramerate(unsigned int framerate, bool vsyncOn = true); - bool isVSyncOn() { return _isVSyncOn; } - bool isVSyncEditable(); - unsigned int getRenderTargetFramerate() const { return _renderTargetFramerate; } - - void setRenderResolutionScale(float scale); + void setVSyncEnabled(bool vsyncOn); void resetSensors(); @@ -624,9 +624,7 @@ private: quint64 _lastNackTime; quint64 _lastSendDownstreamAudioStats; - int _renderTargetFramerate; bool _isVSyncOn; - float _renderResolutionScale; }; #endif // hifi_Application_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f915855758..21c1cded88 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -377,13 +377,12 @@ Menu::Menu() : { QMenu* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate); QActionGroup* framerateGroup = new QActionGroup(framerateMenu); - + framerateGroup->setExclusive(true); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true)); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false)); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false)); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false)); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false)); - connect(framerateMenu, SIGNAL(triggered(QAction*)), this, SLOT(changeRenderTargetFramerate(QAction*))); #if defined(Q_OS_MAC) #else @@ -394,12 +393,12 @@ Menu::Menu() : QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, false)); + resolutionGroup->setExclusive(true); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true)); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false)); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false)); - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, true)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false)); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false)); - connect(resolutionMenu, SIGNAL(triggered(QAction*)), this, SLOT(changeRenderResolution(QAction*))); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, @@ -1261,46 +1260,7 @@ void Menu::muteEnvironment() { } void Menu::changeVSync() { - Application::getInstance()->setRenderTargetFramerate( - Application::getInstance()->getRenderTargetFramerate(), - isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn)); -} -void Menu::changeRenderTargetFramerate(QAction* action) { - bool vsynOn = Application::getInstance()->isVSyncOn(); - - QString text = action->text(); - if (text == MenuOption::RenderTargetFramerateUnlimited) { - Application::getInstance()->setRenderTargetFramerate(0, vsynOn); - } - else if (text == MenuOption::RenderTargetFramerate60) { - Application::getInstance()->setRenderTargetFramerate(60, vsynOn); - } - else if (text == MenuOption::RenderTargetFramerate50) { - Application::getInstance()->setRenderTargetFramerate(50, vsynOn); - } - else if (text == MenuOption::RenderTargetFramerate40) { - Application::getInstance()->setRenderTargetFramerate(40, vsynOn); - } - else if (text == MenuOption::RenderTargetFramerate30) { - Application::getInstance()->setRenderTargetFramerate(30, vsynOn); - } -} - -void Menu::changeRenderResolution(QAction* action) { - QString text = action->text(); - if (text == MenuOption::RenderResolutionOne) { - Application::getInstance()->setRenderResolutionScale(1.f); - } else if (text == MenuOption::RenderResolutionTwoThird) { - Application::getInstance()->setRenderResolutionScale(0.666f); - } else if (text == MenuOption::RenderResolutionHalf) { - Application::getInstance()->setRenderResolutionScale(0.5f); - } else if (text == MenuOption::RenderResolutionThird) { - Application::getInstance()->setRenderResolutionScale(0.333f); - } else if (text == MenuOption::RenderResolutionQuarter) { - Application::getInstance()->setRenderResolutionScale(0.25f); - } else { - Application::getInstance()->setRenderResolutionScale(1.f); - } + Application::getInstance()->setVSyncEnabled(isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn)); } void Menu::displayNameLocationResponse(const QString& errorString) { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 72b43e09c8..8af91ccae4 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -230,9 +230,7 @@ private slots: void displayAddressOfflineMessage(); void displayAddressNotFoundMessage(); void muteEnvironment(); - void changeRenderTargetFramerate(QAction* action); void changeVSync(); - void changeRenderResolution(QAction* action); private: static Menu* _instance; From 92a6609ac00dee85a7eb8b8ef89ab61e4450029a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 3 Nov 2014 11:31:04 -0800 Subject: [PATCH 28/50] simpler tuning system for walk velocities --- interface/src/avatar/MyAvatar.cpp | 53 ++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 623631d5e5..b468f37f33 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1269,6 +1269,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe _isPushing = false; float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); + glm::vec3 newLocalVelocity = localVelocity; float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) + fabsf(_driveKeys[UP] - _driveKeys[DOWN]); @@ -1285,31 +1286,47 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe if (directionLength > EPSILON) { direction /= directionLength; - // Compute the target keyboard velocity (which ramps up slowly, and damps very quickly) - // the max magnitude of which depends on what we're doing: - float motorSpeed = glm::length(_keyboardMotorVelocity); - float finalMaxMotorSpeed = hasFloor ? _scale * MAX_WALKING_SPEED : _scale * MAX_KEYBOARD_MOTOR_SPEED; - float speedGrowthTimescale = 2.0f; - float speedIncreaseFactor = 1.8f; - motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor; - const float maxBoostSpeed = _scale * MAX_BOOST_SPEED; - if (motorSpeed < maxBoostSpeed) { - // an active keyboard motor should never be slower than this - float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed; - motorSpeed += MIN_AVATAR_SPEED * boostCoefficient; - motorEfficiency += (1.0f - motorEfficiency) * boostCoefficient; - } else if (motorSpeed > finalMaxMotorSpeed) { - motorSpeed = finalMaxMotorSpeed; + if (hasFloor) { + // we're walking --> simple exponential decay toward target walk speed + const float WALK_ACCELERATION_TIMESCALE = 0.7f; // seconds to decrease delta to 1/e + _keyboardMotorVelocity = MAX_WALKING_SPEED * direction; + motorEfficiency = glm::clamp(deltaTime / WALK_ACCELERATION_TIMESCALE, 0.0f, 1.0f); + + } else { + // we're flying --> more complex curve + float motorSpeed = glm::length(_keyboardMotorVelocity); + float finalMaxMotorSpeed = _scale * MAX_KEYBOARD_MOTOR_SPEED; + float speedGrowthTimescale = 2.0f; + float speedIncreaseFactor = 1.8f; + motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor; + const float maxBoostSpeed = _scale * MAX_BOOST_SPEED; + if (motorSpeed < maxBoostSpeed) { + // an active keyboard motor should never be slower than this + float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed; + motorSpeed += MIN_AVATAR_SPEED * boostCoefficient; + motorEfficiency += (1.0f - motorEfficiency) * boostCoefficient; + } else if (motorSpeed > finalMaxMotorSpeed) { + motorSpeed = finalMaxMotorSpeed; + } + _keyboardMotorVelocity = motorSpeed * direction; } - _keyboardMotorVelocity = motorSpeed * direction; _isPushing = true; } + newLocalVelocity = localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity); } else { _keyboardMotorVelocity = glm::vec3(0.0f); + newLocalVelocity = (1.0f - motorEfficiency) * localVelocity; + if (hasFloor && !_wasPushing) { + float speed = glm::length(newLocalVelocity); + if (speed > MIN_AVATAR_SPEED) { + // add small constant friction to help avatar drift to a stop sooner at low speeds + const float CONSTANT_FRICTION_DECELERATION = MIN_AVATAR_SPEED / 0.20f; + newLocalVelocity *= (speed - timescale * CONSTANT_FRICTION_DECELERATION) / speed; + } + } } - // apply keyboard motor - return localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity); + return newLocalVelocity; } glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVelocity) { From 948f3a4c1fef6b056595b425ffdc1a0de7ae8d2a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 11:38:49 -0800 Subject: [PATCH 29/50] added an example entity script that makes the avatar do crazy legs when you click on an entity --- examples/entityScripts/crazylegsOnClick.js | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 examples/entityScripts/crazylegsOnClick.js diff --git a/examples/entityScripts/crazylegsOnClick.js b/examples/entityScripts/crazylegsOnClick.js new file mode 100644 index 0000000000..57f16b7e7b --- /dev/null +++ b/examples/entityScripts/crazylegsOnClick.js @@ -0,0 +1,57 @@ +// +// crazylegsOnHover.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to an entity, that entity will make your avatar do the +// crazyLegs dance if you click on it. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function(){ + var cumulativeTime = 0.0; + var FREQUENCY = 5.0; + var AMPLITUDE = 45.0; + var jointList = MyAvatar.getJointNames(); + var jointMappings = "\n# Joint list start"; + for (var i = 0; i < jointList.length; i++) { + jointMappings = jointMappings + "\njointIndex = " + jointList[i] + " = " + i; + } + print(jointMappings + "\n# Joint list end"); + + this.crazyLegsUpdate = function(deltaTime) { + print("crazyLegsUpdate... deltaTime:" + deltaTime); + cumulativeTime += deltaTime; + print("crazyLegsUpdate... cumulativeTime:" + cumulativeTime); + MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY), 0.0, 0.0)); + MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees(-AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY), 0.0, 0.0)); + MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees( + AMPLITUDE * (1.0 + Math.sin(cumulativeTime * FREQUENCY)),0.0, 0.0)); + MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees( + AMPLITUDE * (1.0 - Math.sin(cumulativeTime * FREQUENCY)),0.0, 0.0)); + }; + + this.stopCrazyLegs = function() { + MyAvatar.clearJointData("RightUpLeg"); + MyAvatar.clearJointData("LeftUpLeg"); + MyAvatar.clearJointData("RightLeg"); + MyAvatar.clearJointData("LeftLeg"); + }; + + this.clickDownOnEntity = function(entityID, mouseEvent) { + print("clickDownOnEntity()..."); + cumulativeTime = 0.0; + Script.update.connect(this.crazyLegsUpdate); + }; + + this.clickReleaseOnEntity = function(entityID, mouseEvent) { + print("clickReleaseOnEntity()..."); + this.stopCrazyLegs(); + Script.update.disconnect(this.crazyLegsUpdate); + }; +}) + From f9bd954fbe2f407678c3bbcd203ae59b84864392 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 11:43:30 -0800 Subject: [PATCH 30/50] typo --- examples/entityScripts/crazylegsOnClick.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/entityScripts/crazylegsOnClick.js b/examples/entityScripts/crazylegsOnClick.js index 57f16b7e7b..149c8bf43e 100644 --- a/examples/entityScripts/crazylegsOnClick.js +++ b/examples/entityScripts/crazylegsOnClick.js @@ -1,5 +1,5 @@ // -// crazylegsOnHover.js +// crazylegsOnClick.js // examples/entityScripts // // Created by Brad Hefta-Gaub on 11/3/14. From e976942515f6666699bf80ae9933f7ae0fbb2e4a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 11:44:15 -0800 Subject: [PATCH 31/50] fix includes for HifiSockAddr --- libraries/networking/src/HifiSockAddr.cpp | 6 +++--- libraries/networking/src/HifiSockAddr.h | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index 2cb0aaad83..3a200fd392 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -9,9 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include +#include +#include +#include #include "HifiSockAddr.h" diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 50dc99f4df..0aa6779589 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -19,7 +19,6 @@ #include #endif -#include #include class HifiSockAddr : public QObject { From 30c5451b877b0316649fe56104988c5bdee8e396 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 11:52:16 -0800 Subject: [PATCH 32/50] added example of playing a sound on click --- examples/entityScripts/playSoundOnClick.js | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 examples/entityScripts/playSoundOnClick.js diff --git a/examples/entityScripts/playSoundOnClick.js b/examples/entityScripts/playSoundOnClick.js new file mode 100644 index 0000000000..b261bb269a --- /dev/null +++ b/examples/entityScripts/playSoundOnClick.js @@ -0,0 +1,24 @@ +// +// playSoundOnClick.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to an entity, that entity will play a sound in world when +// you click on it. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +(function(){ + var bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); + this.clickDownOnEntity = function(entityID, mouseEvent) { + print("clickDownOnEntity()..."); + var options = new AudioInjectionOptions(); + var position = MyAvatar.position; + options.position = position; + options.volume = 0.5; + Audio.playSound(bird, options); + }; +}) From 252d05282b9618580665819743cec35f0c3c8c57 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 12:03:08 -0800 Subject: [PATCH 33/50] added example entity script that teleports your avatar to the entity when you click on it --- examples/entityScripts/teleportOnClick.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 examples/entityScripts/teleportOnClick.js diff --git a/examples/entityScripts/teleportOnClick.js b/examples/entityScripts/teleportOnClick.js new file mode 100644 index 0000000000..11677b12d5 --- /dev/null +++ b/examples/entityScripts/teleportOnClick.js @@ -0,0 +1,18 @@ +// +// teleportOnClick.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to an entity, that entity will teleport your avatar if you +// click on it the entity. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +(function(){ + this.clickDownOnEntity = function(entityID, mouseEvent) { + MyAvatar.position = Entities.getEntityProperties(entityID).position; + }; +}) From 9e1da6489534ea85409840c1d60c7f977355b740 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:03:10 -0800 Subject: [PATCH 34/50] use different include style for QHostInfo --- libraries/networking/src/HifiSockAddr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 0aa6779589..5bbd27437b 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -19,7 +19,7 @@ #include #endif -#include +#include class HifiSockAddr : public QObject { Q_OBJECT From e53ad09b3f8218b4465591e6f64c237f5872b123 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:24:17 -0800 Subject: [PATCH 35/50] rename the automatic networking update method to heartbeat --- domain-server/src/DomainServer.cpp | 16 ++++++++-------- domain-server/src/DomainServer.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f99c1d280e..22c194ffb0 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -361,11 +361,11 @@ void DomainServer::setupAutomaticNetworking() { iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS); // call our sendHeartbeaToIceServer immediately anytime a local or public socket changes - connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer); - connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer); + connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); + connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); // tell the data server which type of automatic networking we are using - updateNetworkingInfoWithDataServer(automaticNetworkValue); + sendHeartbeatToDataServer(automaticNetworkValue); } // attempt to update our sockets now @@ -378,7 +378,7 @@ void DomainServer::setupAutomaticNetworking() { return; } } else { - updateNetworkingInfoWithDataServer(automaticNetworkValue); + sendHeartbeatToDataServer(automaticNetworkValue); } } @@ -1081,10 +1081,10 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) { const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking"; void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) { - updateNetworkingInfoWithDataServer(IP_ONLY_AUTOMATIC_NETWORKING_VALUE, newPublicSockAddr.getAddress().toString()); + sendHeartbeatToDataServer(IP_ONLY_AUTOMATIC_NETWORKING_VALUE, newPublicSockAddr.getAddress().toString()); } -void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress) { +void DomainServer::sendHeartbeatToDataServer(const QString& newSetting, const QString& networkAddress) { const QString DOMAIN_UPDATE = "/api/v1/domains/%1"; const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID(); @@ -1112,11 +1112,11 @@ void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting, // todo: have data-web respond with ice-server hostname to use void DomainServer::performICEUpdates() { - sendHearbeatToIceServer(); + sendHeartbeatToIceServer(); sendICEPingPackets(); } -void DomainServer::sendHearbeatToIceServer() { +void DomainServer::sendHeartbeatToIceServer() { const HifiSockAddr ICE_SERVER_SOCK_ADDR = HifiSockAddr("ice.highfidelity.io", ICE_SERVER_DEFAULT_PORT); LimitedNodeList::getInstance()->sendHeartbeatToIceServer(ICE_SERVER_SOCK_ADDR); } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 5e4da00601..13bc4a0474 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -66,7 +66,8 @@ private slots: void requestCurrentPublicSocketViaSTUN(); void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); void performICEUpdates(); - void sendHearbeatToIceServer(); + void sendHeartbeatToDataServer(const QString& newSetting, const QString& networkAddress = QString()); + void sendHeartbeatToIceServer(); void sendICEPingPackets(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); @@ -76,7 +77,6 @@ private: bool optionallySetupAssignmentPayment(); void setupAutomaticNetworking(); - void updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress = QString()); void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr); void processICEHeartbeatResponse(const QByteArray& packet); From d50a8f630982f8555d9f6cc9a2fe612f04902e95 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:40:56 -0800 Subject: [PATCH 36/50] send a heartbeat to data server every 15s --- domain-server/src/DomainServer.cpp | 34 ++++++++++++++++++------------ domain-server/src/DomainServer.h | 5 ++++- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 22c194ffb0..3cea933246 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -50,6 +50,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _hostname(), _webAuthenticationStateSet(), _cookieSessionHash(), + _automaticNetworkingSetting(), _settingsManager() { LogUtils::init(); @@ -327,17 +328,17 @@ void DomainServer::setupAutomaticNetworking() { return; } - QString automaticNetworkValue = + _automaticNetworkingSetting = _settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString(); - if (automaticNetworkValue == IP_ONLY_AUTOMATIC_NETWORKING_VALUE || - automaticNetworkValue == FULL_AUTOMATIC_NETWORKING_VALUE) { + if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE || + _automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { LimitedNodeList* nodeList = LimitedNodeList::getInstance(); const QUuid& domainID = nodeList->getSessionUUID(); if (!domainID.isNull()) { - qDebug() << "domain-server" << automaticNetworkValue << "automatic networking enabled for ID" + qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID" << uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString(); const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000; @@ -347,7 +348,7 @@ void DomainServer::setupAutomaticNetworking() { QTimer* dynamicIPTimer = new QTimer(this); connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN); - if (automaticNetworkValue == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) { + if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) { dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS); // send public socket changes to the data server so nodes can find us at our new IP @@ -364,8 +365,8 @@ void DomainServer::setupAutomaticNetworking() { connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); - // tell the data server which type of automatic networking we are using - sendHeartbeatToDataServer(automaticNetworkValue); + // send our heartbeat to data server so it knows what our network settings are + sendHeartbeatToDataServer(); } // attempt to update our sockets now @@ -378,8 +379,17 @@ void DomainServer::setupAutomaticNetworking() { return; } } else { - sendHeartbeatToDataServer(automaticNetworkValue); + sendHeartbeatToDataServer(); } + + qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting; + + // no matter the auto networking settings we should heartbeat to the data-server every 15s + const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_SECONDS = 15; + + QTimer* dataHeartbeatTimer = new QTimer(this); + connect(dataHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToDataServer); + dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_SECONDS); } void DomainServer::loginFailed() { @@ -1081,10 +1091,10 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) { const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking"; void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) { - sendHeartbeatToDataServer(IP_ONLY_AUTOMATIC_NETWORKING_VALUE, newPublicSockAddr.getAddress().toString()); + updateDomainInDataServer(newPublicSockAddr.getAddress().toString()); } -void DomainServer::sendHeartbeatToDataServer(const QString& newSetting, const QString& networkAddress) { +void DomainServer::updateDomainInDataServer(const QString& networkAddress) { const QString DOMAIN_UPDATE = "/api/v1/domains/%1"; const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID(); @@ -1097,9 +1107,7 @@ void DomainServer::sendHeartbeatToDataServer(const QString& newSetting, const QS domainObject[PUBLIC_NETWORK_ADDRESS_KEY] = networkAddress; } - qDebug() << "Updating automatic networking setting in domain-server to" << newSetting; - - domainObject[AUTOMATIC_NETWORKING_KEY] = newSetting; + domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting; QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson())); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 13bc4a0474..e5f321639c 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -66,7 +66,7 @@ private slots: void requestCurrentPublicSocketViaSTUN(); void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); void performICEUpdates(); - void sendHeartbeatToDataServer(const QString& newSetting, const QString& networkAddress = QString()); + void sendHeartbeatToDataServer() { updateDomainInDataServer(); } void sendHeartbeatToIceServer(); void sendICEPingPackets(); private: @@ -77,6 +77,7 @@ private: bool optionallySetupAssignmentPayment(); void setupAutomaticNetworking(); + void updateDomainInDataServer(const QString& networkAddress = QString()); void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr); void processICEHeartbeatResponse(const QByteArray& packet); @@ -150,6 +151,8 @@ private: QHash _connectingICEPeers; QHash _connectedICEPeers; + QString _automaticNetworkingSetting; + DomainServerSettingsManager _settingsManager; }; From eb856095a962033a7984c69f7285d5a435b947ff Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:49:24 -0800 Subject: [PATCH 37/50] fix the domain server heartbeat timer --- domain-server/src/DomainServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 3cea933246..c6ab0285ea 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -385,11 +385,11 @@ void DomainServer::setupAutomaticNetworking() { qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting; // no matter the auto networking settings we should heartbeat to the data-server every 15s - const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_SECONDS = 15; + const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000; QTimer* dataHeartbeatTimer = new QTimer(this); connect(dataHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToDataServer); - dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_SECONDS); + dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS); } void DomainServer::loginFailed() { From fabc3aea5709960744a72f187c86573bb5351302 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 12:54:28 -0800 Subject: [PATCH 38/50] Revert "don't allow a HifiSockAddr lookup to block application" --- libraries/networking/src/HifiSockAddr.cpp | 39 ++++++-------------- libraries/networking/src/HifiSockAddr.h | 7 +--- libraries/networking/src/LimitedNodeList.cpp | 6 ++- libraries/networking/src/LimitedNodeList.h | 1 - 4 files changed, 17 insertions(+), 36 deletions(-) diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index 3a200fd392..97e9721356 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -9,9 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include +#include +#include +#include #include "HifiSockAddr.h" @@ -36,15 +36,14 @@ HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) { _port = otherSockAddr._port; } -HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) : - _address(hostname), - _port(hostOrderPort) -{ - // if we parsed an IPv4 address out of the hostname, don't look it up - if (_address.protocol() != QAbstractSocket::IPv4Protocol) { - // sync lookup the IP by the hostname - int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo))); - qDebug() << "Looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; +HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) { + // lookup the IP by the hostname + QHostInfo hostInfo = QHostInfo::fromName(hostname); + foreach(const QHostAddress& address, hostInfo.addresses()) { + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + _address = address; + _port = hostOrderPort; + } } } @@ -76,22 +75,6 @@ bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const { return _address == rhsSockAddr._address && _port == rhsSockAddr._port; } -void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) { - if (hostInfo.error() != QHostInfo::NoError) { - qDebug() << "Lookup failed for" << hostInfo.lookupId() << ":" << hostInfo.errorString(); - } - - foreach(const QHostAddress& address, hostInfo.addresses()) { - // just take the first IPv4 address - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - _address = address; - qDebug() << "QHostInfo lookup result for" - << hostInfo.hostName() << "with lookup ID" << hostInfo.lookupId() << "is" << address.toString(); - break; - } - } -} - QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) { debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port; return debug.space(); diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 5bbd27437b..42f815390a 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -19,10 +19,9 @@ #include #endif -#include +#include -class HifiSockAddr : public QObject { - Q_OBJECT +class HifiSockAddr { public: HifiSockAddr(); HifiSockAddr(const QHostAddress& address, quint16 port); @@ -52,8 +51,6 @@ public: friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr); friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr); friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr); -private slots: - void handleLookupResult(const QHostInfo& hostInfo); private: QHostAddress _address; quint16 _port; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 043f0621bb..5c4dc6cea2 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -73,7 +73,6 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short _dtlsSocket(NULL), _localSockAddr(), _publicSockAddr(), - _stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT), _numCollectedPackets(0), _numCollectedBytes(0), _packetStatTimer() @@ -584,8 +583,11 @@ void LimitedNodeList::sendSTUNRequest() { QUuid randomUUID = QUuid::createUuid(); memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); + // lookup the IP for the STUN server + HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); + _nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket), - _stunSockAddr.getAddress(), _stunSockAddr.getPort()); + stunSockAddr.getAddress(), stunSockAddr.getPort()); } void LimitedNodeList::rebindNodeSocket() { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 64495fbd34..73381d01a5 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -163,7 +163,6 @@ protected: QUdpSocket* _dtlsSocket; HifiSockAddr _localSockAddr; HifiSockAddr _publicSockAddr; - HifiSockAddr _stunSockAddr; int _numCollectedPackets; int _numCollectedBytes; QElapsedTimer _packetStatTimer; From a4bd0acc9100336d9421e8711cfa51930a9346e8 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 3 Nov 2014 14:36:03 -0800 Subject: [PATCH 39/50] Fix the syntax issue with if blocks --- interface/src/Application.cpp | 66 +++++++++++++++-------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 11dd64f07a..231e535a33 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4233,27 +4233,27 @@ void Application::takeSnapshot() { void Application::setVSyncEnabled(bool vsyncOn) { #if defined(Q_OS_WIN) - if (wglewGetExtension("WGL_EXT_swap_control")) { - wglSwapIntervalEXT(vsyncOn); - int swapInterval = wglGetSwapIntervalEXT(); - qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } else { - qDebug("V-Sync is FORCED ON on this system\n"); - } + if (wglewGetExtension("WGL_EXT_swap_control")) { + wglSwapIntervalEXT(vsyncOn); + int swapInterval = wglGetSwapIntervalEXT(); + qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); + } else { + qDebug("V-Sync is FORCED ON on this system\n"); + } #elif defined(Q_OS_LINUX) - // TODO: write the poper code for linux - /* - if (glQueryExtension.... ("GLX_EXT_swap_control")) { - glxSwapIntervalEXT(vsyncOn); - int swapInterval = xglGetSwapIntervalEXT(); - _isVSyncOn = swapInterval; - qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } else { - qDebug("V-Sync is FORCED ON on this system\n"); - } - */ + // TODO: write the poper code for linux + /* + if (glQueryExtension.... ("GLX_EXT_swap_control")) { + glxSwapIntervalEXT(vsyncOn); + int swapInterval = xglGetSwapIntervalEXT(); + _isVSyncOn = swapInterval; + qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); + } else { + qDebug("V-Sync is FORCED ON on this system\n"); + } + */ #else - qDebug("V-Sync is FORCED ON on this system\n"); + qDebug("V-Sync is FORCED ON on this system\n"); #endif } @@ -4300,40 +4300,30 @@ bool Application::isVSyncEditable() const { unsigned int Application::getRenderTargetFramerate() const { if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateUnlimited)) { return 0; - } - else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate60)) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate60)) { return 60; - } - else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate50)) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate50)) { return 50; - } - else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate40)) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate40)) { return 40; - } - else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate30)) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate30)) { return 30; } return 0; } float Application::getRenderResolutionScale() const { - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) { return 1.f; - } - else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionTwoThird)) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionTwoThird)) { return 0.666f; - } - else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionHalf)) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionHalf)) { return 0.5f; - } - else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionThird)) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionThird)) { return 0.333f; - } - else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionQuarter)) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionQuarter)) { return 0.25f; - } - else { + } else { return 1.f; } } \ No newline at end of file From 22f4e8ec70de07655644ea44bba6ab77864828c7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 14:37:56 -0800 Subject: [PATCH 40/50] cleanup heartbeat method names --- domain-server/src/DomainServer.cpp | 6 +++--- domain-server/src/DomainServer.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c6ab0285ea..ed0fd60ae5 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -388,7 +388,7 @@ void DomainServer::setupAutomaticNetworking() { const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000; QTimer* dataHeartbeatTimer = new QTimer(this); - connect(dataHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToDataServer); + connect(dataHeartbeatTimer, SIGNAL(timeout()), this, SLOT(sendHeartbeatToDataServer())); dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS); } @@ -1091,10 +1091,10 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) { const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking"; void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) { - updateDomainInDataServer(newPublicSockAddr.getAddress().toString()); + sendHeartbeatToDataServer(newPublicSockAddr.getAddress().toString()); } -void DomainServer::updateDomainInDataServer(const QString& networkAddress) { +void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) { const QString DOMAIN_UPDATE = "/api/v1/domains/%1"; const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index e5f321639c..de485da5e7 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -66,7 +66,7 @@ private slots: void requestCurrentPublicSocketViaSTUN(); void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); void performICEUpdates(); - void sendHeartbeatToDataServer() { updateDomainInDataServer(); } + void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); } void sendHeartbeatToIceServer(); void sendICEPingPackets(); private: @@ -77,7 +77,7 @@ private: bool optionallySetupAssignmentPayment(); void setupAutomaticNetworking(); - void updateDomainInDataServer(const QString& networkAddress = QString()); + void sendHeartbeatToDataServer(const QString& networkAddress); void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr); void processICEHeartbeatResponse(const QByteArray& packet); From 4d1fbc1cf9e1091bce5e64f3fd10a543d83dbf7a Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 3 Nov 2014 14:39:34 -0800 Subject: [PATCH 41/50] Fix end of line in Application.cpp --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 231e535a33..f35336817b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4326,4 +4326,4 @@ float Application::getRenderResolutionScale() const { } else { return 1.f; } -} \ No newline at end of file +} From 2250cdfcc2a750f9778925f7d89234c60997d78f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 14:50:45 -0800 Subject: [PATCH 42/50] have the domain-server unverifiably heartbeat the number of connected users --- domain-server/src/DomainServer.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ed0fd60ae5..198c61bcba 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1109,8 +1109,25 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) { domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting; + // add the number of currently connected agent users + int numConnectedAuthedUsers = 0; + foreach(const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { + if (node->getLinkedData() && !static_cast(node->getLinkedData())->getUsername().isEmpty()) { + ++numConnectedAuthedUsers; + } + } + + const QString DOMAIN_HEARTBEAT_KEY = "heartbeat"; + const QString HEARTBEAT_NUM_USERS_KEY = "num_users"; + + QJsonObject heartbeatObject; + heartbeatObject[HEARTBEAT_NUM_USERS_KEY] = numConnectedAuthedUsers; + domainObject[DOMAIN_HEARTBEAT_KEY] = heartbeatObject; + QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson())); + qDebug() << domainUpdateJSON; + AccountManager::getInstance().authenticatedRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)), QNetworkAccessManager::PutOperation, JSONCallbackParameters(), From acef1b868c879277b912584cdf7eeb4bbd43503f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 15:05:40 -0800 Subject: [PATCH 43/50] Revert "Revert "don't allow a HifiSockAddr lookup to block application"" This reverts commit fabc3aea5709960744a72f187c86573bb5351302. --- libraries/networking/src/HifiSockAddr.cpp | 39 ++++++++++++++------ libraries/networking/src/HifiSockAddr.h | 7 +++- libraries/networking/src/LimitedNodeList.cpp | 6 +-- libraries/networking/src/LimitedNodeList.h | 1 + 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index 97e9721356..3a200fd392 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -9,9 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include +#include +#include +#include #include "HifiSockAddr.h" @@ -36,14 +36,15 @@ HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) { _port = otherSockAddr._port; } -HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) { - // lookup the IP by the hostname - QHostInfo hostInfo = QHostInfo::fromName(hostname); - foreach(const QHostAddress& address, hostInfo.addresses()) { - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - _address = address; - _port = hostOrderPort; - } +HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) : + _address(hostname), + _port(hostOrderPort) +{ + // if we parsed an IPv4 address out of the hostname, don't look it up + if (_address.protocol() != QAbstractSocket::IPv4Protocol) { + // sync lookup the IP by the hostname + int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo))); + qDebug() << "Looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; } } @@ -75,6 +76,22 @@ bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const { return _address == rhsSockAddr._address && _port == rhsSockAddr._port; } +void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) { + if (hostInfo.error() != QHostInfo::NoError) { + qDebug() << "Lookup failed for" << hostInfo.lookupId() << ":" << hostInfo.errorString(); + } + + foreach(const QHostAddress& address, hostInfo.addresses()) { + // just take the first IPv4 address + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + _address = address; + qDebug() << "QHostInfo lookup result for" + << hostInfo.hostName() << "with lookup ID" << hostInfo.lookupId() << "is" << address.toString(); + break; + } + } +} + QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) { debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port; return debug.space(); diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 42f815390a..5bbd27437b 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -19,9 +19,10 @@ #include #endif -#include +#include -class HifiSockAddr { +class HifiSockAddr : public QObject { + Q_OBJECT public: HifiSockAddr(); HifiSockAddr(const QHostAddress& address, quint16 port); @@ -51,6 +52,8 @@ public: friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr); friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr); friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr); +private slots: + void handleLookupResult(const QHostInfo& hostInfo); private: QHostAddress _address; quint16 _port; diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 5c4dc6cea2..043f0621bb 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -73,6 +73,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short _dtlsSocket(NULL), _localSockAddr(), _publicSockAddr(), + _stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT), _numCollectedPackets(0), _numCollectedBytes(0), _packetStatTimer() @@ -583,11 +584,8 @@ void LimitedNodeList::sendSTUNRequest() { QUuid randomUUID = QUuid::createUuid(); memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); - // lookup the IP for the STUN server - HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); - _nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket), - stunSockAddr.getAddress(), stunSockAddr.getPort()); + _stunSockAddr.getAddress(), _stunSockAddr.getPort()); } void LimitedNodeList::rebindNodeSocket() { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 73381d01a5..64495fbd34 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -163,6 +163,7 @@ protected: QUdpSocket* _dtlsSocket; HifiSockAddr _localSockAddr; HifiSockAddr _publicSockAddr; + HifiSockAddr _stunSockAddr; int _numCollectedPackets; int _numCollectedBytes; QElapsedTimer _packetStatTimer; From 74753e5b8bc66c168cb8b432e7d1602dd5766931 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 15:13:41 -0800 Subject: [PATCH 44/50] allow a blocking lookup of IP address in HifiSockAddr --- assignment-client/src/AssignmentClient.cpp | 7 ++----- libraries/networking/src/HifiSockAddr.cpp | 13 +++++++++---- libraries/networking/src/HifiSockAddr.h | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index e7ac7577b9..929d6c76c8 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -96,17 +96,14 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : assignmentServerPort = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt(); } - - HifiSockAddr assignmentServerSocket(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME, assignmentServerPort); // check for an overriden assignment server hostname if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) { - _assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString(); - // change the hostname for our assignment server - assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerSocket.getPort()); + _assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString(); } + HifiSockAddr assignmentServerSocket(_assignmentServerHostname, assignmentServerPort, true); nodeList->setAssignmentServerSocket(assignmentServerSocket); qDebug() << "Assignment server socket is" << assignmentServerSocket; diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index 3a200fd392..5e7ad3a2da 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -36,15 +36,20 @@ HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) { _port = otherSockAddr._port; } -HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) : +HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup) : _address(hostname), _port(hostOrderPort) { // if we parsed an IPv4 address out of the hostname, don't look it up if (_address.protocol() != QAbstractSocket::IPv4Protocol) { - // sync lookup the IP by the hostname - int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo))); - qDebug() << "Looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; + // lookup the IP by the hostname + if (shouldBlockForLookup) { + QHostInfo result = QHostInfo::fromName(hostname); + handleLookupResult(result); + } else { + int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo))); + qDebug() << "Looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; + } } } diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index 5bbd27437b..064f8032ca 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -27,7 +27,7 @@ public: HifiSockAddr(); HifiSockAddr(const QHostAddress& address, quint16 port); HifiSockAddr(const HifiSockAddr& otherSockAddr); - HifiSockAddr(const QString& hostname, quint16 hostOrderPort); + HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false); HifiSockAddr(const sockaddr* sockaddr); bool isNull() const { return _address.isNull() && _port == 0; } From 88300f06ad6b775ef21f1e54415ae8c8a51caa30 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 15:14:39 -0800 Subject: [PATCH 45/50] cleanup some debug in HifiSockAddr for hostname lookup --- libraries/networking/src/HifiSockAddr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/HifiSockAddr.cpp b/libraries/networking/src/HifiSockAddr.cpp index 5e7ad3a2da..11674a948e 100644 --- a/libraries/networking/src/HifiSockAddr.cpp +++ b/libraries/networking/src/HifiSockAddr.cpp @@ -44,11 +44,12 @@ HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool if (_address.protocol() != QAbstractSocket::IPv4Protocol) { // lookup the IP by the hostname if (shouldBlockForLookup) { + qDebug() << "Asynchronously looking up IP address for hostname" << hostname; QHostInfo result = QHostInfo::fromName(hostname); handleLookupResult(result); } else { int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo))); - qDebug() << "Looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; + qDebug() << "Synchronously looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID; } } } From 0348251cc121d2fd7a50a9c37c13bdec951031df Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 3 Nov 2014 15:32:45 -0800 Subject: [PATCH 46/50] allow case insensitivity for allowed users in domain-server --- domain-server/src/DomainServer.cpp | 6 ++---- libraries/networking/src/DataServerAccountInfo.cpp | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 198c61bcba..131aa3fbe3 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -637,7 +637,7 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username, } if (allowedUsers.count() > 0) { - if (allowedUsers.contains(username)) { + if (allowedUsers.contains(username, Qt::CaseInsensitive)) { // it's possible this user can be allowed to connect, but we need to check their username signature QByteArray publicKeyArray = _userPublicKeys.value(username); @@ -657,7 +657,7 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username, rsaPublicKey, RSA_PKCS1_PADDING); if (decryptResult != -1) { - if (username == decryptedArray) { + if (username.toLower() == decryptedArray) { qDebug() << "Username signature matches for" << username << "- allowing connection."; // free up the public key before we return @@ -1126,8 +1126,6 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) { QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson())); - qDebug() << domainUpdateJSON; - AccountManager::getInstance().authenticatedRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)), QNetworkAccessManager::PutOperation, JSONCallbackParameters(), diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 600eb57166..9b513c96f1 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -130,11 +130,11 @@ const QByteArray& DataServerAccountInfo::getUsernameSignature() { reinterpret_cast(&privateKeyData), _privateKey.size()); if (rsaPrivateKey) { - QByteArray usernameByteArray = _username.toUtf8(); + QByteArray lowercaseUsername = _username.toLower().toUtf8(); _usernameSignature.resize(RSA_size(rsaPrivateKey)); - int encryptReturn = RSA_private_encrypt(usernameByteArray.size(), - reinterpret_cast(usernameByteArray.constData()), + int encryptReturn = RSA_private_encrypt(lowercaseUsername.size(), + reinterpret_cast(lowercaseUsername.constData()), reinterpret_cast(_usernameSignature.data()), rsaPrivateKey, RSA_PKCS1_PADDING); From e12e3b05e10b72848ed131cfc413cf9ef484f9d1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 15:33:01 -0800 Subject: [PATCH 47/50] add support for enter/leave entity events for when the avatar enters or leaves the bounds of an entity --- .../entityScripts/playSoundOnEnterOrLeave.js | 32 +++++++++ interface/src/entities/EntityTreeRenderer.cpp | 66 +++++++++++++++++++ interface/src/entities/EntityTreeRenderer.h | 9 +++ libraries/entities/src/EntityItem.h | 1 + .../entities/src/EntityScriptingInterface.h | 3 + libraries/entities/src/SphereEntityItem.h | 3 + 6 files changed, 114 insertions(+) create mode 100644 examples/entityScripts/playSoundOnEnterOrLeave.js diff --git a/examples/entityScripts/playSoundOnEnterOrLeave.js b/examples/entityScripts/playSoundOnEnterOrLeave.js new file mode 100644 index 0000000000..6f2ec830c6 --- /dev/null +++ b/examples/entityScripts/playSoundOnEnterOrLeave.js @@ -0,0 +1,32 @@ +// +// playSoundOnEnterOrLeave.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to an entity, that entity will play a sound in world when +// your avatar enters or leaves the bounds of the entity. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +(function(){ + var bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); + + function playSound(entityID) { + var options = new AudioInjectionOptions(); + var position = MyAvatar.position; + options.position = position; + options.volume = 0.5; + Audio.playSound(bird, options); + }; + + this.enterEntity = function(entityID) { + playSound(); + }; + + this.leaveEntity = function(entityID) { + playSound(); + }; +}) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index df2c51104b..4963c8baca 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -72,6 +72,11 @@ void EntityTreeRenderer::init() { Application::getInstance()->getControllerScriptingInterface()); Application::getInstance()->registerScriptEngineWithApplicationServices(_entitiesScriptEngine); } + + // make sure our "last avatar position" is something other than our current position, so that on our + // first chance, we'll check for enter/leave entity events. + glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition(); + _lastAvatarPosition = avatarPosition + glm::vec3(1.f,1.f,1.f); } QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) { @@ -183,6 +188,58 @@ void EntityTreeRenderer::update() { if (_tree) { EntityTree* tree = static_cast(_tree); tree->update(); + + // check to see if the avatar has moved and if we need to handle enter/leave entity logic + checkEnterLeaveEntities(); + } +} + +void EntityTreeRenderer::checkEnterLeaveEntities() { + if (_tree) { + glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition() / (float)TREE_SCALE; + + if (avatarPosition != _lastAvatarPosition) { + float radius = 1.0f / (float)TREE_SCALE; // for now, assume 1 meter radius + QVector foundEntities; + QVector entitiesContainingAvatar; + + // find the entities near us + static_cast(_tree)->findEntities(avatarPosition, radius, foundEntities); + + // create a list of entities that actually contain the avatar's position + foreach(const EntityItem* entity, foundEntities) { + if (entity->contains(avatarPosition)) { + entitiesContainingAvatar << entity->getEntityItemID(); + } + } + + // for all of our previous containing entities, if they are no longer containing then send them a leave event + foreach(const EntityItemID& entityID, _currentEntitiesInside) { + if (!entitiesContainingAvatar.contains(entityID)) { + emit leaveEntity(entityID); + QScriptValueList entityArgs = createEntityArgs(entityID); + QScriptValue entityScript = loadEntityScript(entityID); + if (entityScript.property("leaveEntity").isValid()) { + entityScript.property("leaveEntity").call(entityScript, entityArgs); + } + + } + } + + // for all of our new containing entities, if they weren't previously containing then send them an enter event + foreach(const EntityItemID& entityID, entitiesContainingAvatar) { + if (!_currentEntitiesInside.contains(entityID)) { + emit enterEntity(entityID); + QScriptValueList entityArgs = createEntityArgs(entityID); + QScriptValue entityScript = loadEntityScript(entityID); + if (entityScript.property("enterEntity").isValid()) { + entityScript.property("enterEntity").call(entityScript, entityArgs); + } + } + } + _currentEntitiesInside = entitiesContainingAvatar; + _lastAvatarPosition = avatarPosition; + } } } @@ -547,6 +604,9 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS connect(this, &EntityTreeRenderer::hoverEnterEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity); connect(this, &EntityTreeRenderer::hoverOverEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); connect(this, &EntityTreeRenderer::hoverLeaveEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); + + connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity); + connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity); } QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) { @@ -556,6 +616,12 @@ QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& en return args; } +QScriptValueList EntityTreeRenderer::createEntityArgs(const EntityItemID& entityID) { + QScriptValueList args; + args << entityID.toScriptValue(_entitiesScriptEngine); + return args; +} + void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); PickRay ray = computePickRay(event->x(), event->y()); diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 6c3f948594..47a4836738 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -104,6 +104,9 @@ signals: void hoverEnterEntity(const EntityItemID& entityItemID, const MouseEvent& event); void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event); void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event); + + void enterEntity(const EntityItemID& entityItemID); + void leaveEntity(const EntityItemID& entityItemID); private: QList _releasedModels; @@ -113,6 +116,12 @@ private: EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; + + + QScriptValueList createEntityArgs(const EntityItemID& entityID); + void checkEnterLeaveEntities(); + glm::vec3 _lastAvatarPosition; + QVector _currentEntitiesInside; bool _wantScripts; ScriptEngine* _entitiesScriptEngine; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 65da3c964a..2117ec25b0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -253,6 +253,7 @@ public: void applyHardCollision(const CollisionInfo& collisionInfo); virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; } + virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); } protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 1152ccbd03..2150fa51da 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -115,6 +115,9 @@ signals: void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event); void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void enterEntity(const EntityItemID& entityItemID); + void leaveEntity(const EntityItemID& entityItemID); + private: void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h index d57ada760b..21cb58223b 100644 --- a/libraries/entities/src/SphereEntityItem.h +++ b/libraries/entities/src/SphereEntityItem.h @@ -53,6 +53,9 @@ public: virtual const Shape& getCollisionShapeInMeters() const { return _sphereShape; } + // TODO: implement proper contains for 3D ellipsoid + //virtual bool contains(const glm::vec3& point) const; + protected: virtual void recalculateCollisionShape(); From bc329a63945ab2ef3775e3fd999354400d09766f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 15:34:30 -0800 Subject: [PATCH 48/50] remove blank line --- interface/src/entities/EntityTreeRenderer.h | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 47a4836738..0a725c8294 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -117,7 +117,6 @@ private: EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; - QScriptValueList createEntityArgs(const EntityItemID& entityID); void checkEnterLeaveEntities(); glm::vec3 _lastAvatarPosition; From 2dae78b82c8e27b01228292ee456f6f137370a0d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 15:40:09 -0800 Subject: [PATCH 49/50] CR feedback --- interface/src/entities/EntityTreeRenderer.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 4963c8baca..693b367d73 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -196,10 +196,10 @@ void EntityTreeRenderer::update() { void EntityTreeRenderer::checkEnterLeaveEntities() { if (_tree) { - glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition() / (float)TREE_SCALE; + glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition() / (float) TREE_SCALE; if (avatarPosition != _lastAvatarPosition) { - float radius = 1.0f / (float)TREE_SCALE; // for now, assume 1 meter radius + float radius = 1.0f / (float) TREE_SCALE; // for now, assume 1 meter radius QVector foundEntities; QVector entitiesContainingAvatar; @@ -276,8 +276,8 @@ const Model* EntityTreeRenderer::getModelForEntityItem(const EntityItem* entityI } void renderElementProxy(EntityTreeElement* entityTreeElement) { - glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float)TREE_SCALE; - float elementSize = entityTreeElement->getScale() * (float)TREE_SCALE; + glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float) TREE_SCALE; + float elementSize = entityTreeElement->getScale() * (float) TREE_SCALE; glColor3f(1.0f, 0.0f, 0.0f); glPushMatrix(); glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z); @@ -350,9 +350,9 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg AACube minCube = entity->getMinimumAACube(); AABox entityBox = entity->getAABox(); - maxCube.scale((float)TREE_SCALE); - minCube.scale((float)TREE_SCALE); - entityBox.scale((float)TREE_SCALE); + maxCube.scale((float) TREE_SCALE); + minCube.scale((float) TREE_SCALE); + entityBox.scale((float) TREE_SCALE); glm::vec3 maxCenter = maxCube.calcCenter(); glm::vec3 minCenter = minCube.calcCenter(); @@ -382,9 +382,9 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg glPopMatrix(); - glm::vec3 position = entity->getPosition() * (float)TREE_SCALE; - glm::vec3 center = entity->getCenter() * (float)TREE_SCALE; - glm::vec3 dimensions = entity->getDimensions() * (float)TREE_SCALE; + glm::vec3 position = entity->getPosition() * (float) TREE_SCALE; + glm::vec3 center = entity->getCenter() * (float) TREE_SCALE; + glm::vec3 dimensions = entity->getDimensions() * (float) TREE_SCALE; glm::quat rotation = entity->getRotation(); glColor4f(1.0f, 0.0f, 1.0f, 1.0f); From 60182a78cb0050cd996c0d61cba396b9ae496b56 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 15:42:40 -0800 Subject: [PATCH 50/50] CR feedback --- examples/entityScripts/playSoundOnEnterOrLeave.js | 10 +++++----- interface/src/entities/EntityTreeRenderer.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/entityScripts/playSoundOnEnterOrLeave.js b/examples/entityScripts/playSoundOnEnterOrLeave.js index 6f2ec830c6..228a8a36d0 100644 --- a/examples/entityScripts/playSoundOnEnterOrLeave.js +++ b/examples/entityScripts/playSoundOnEnterOrLeave.js @@ -15,11 +15,11 @@ var bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); function playSound(entityID) { - var options = new AudioInjectionOptions(); - var position = MyAvatar.position; - options.position = position; - options.volume = 0.5; - Audio.playSound(bird, options); + var options = new AudioInjectionOptions(); + var position = MyAvatar.position; + options.position = position; + options.volume = 0.5; + Audio.playSound(bird, options); }; this.enterEntity = function(entityID) { diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 693b367d73..e447c703fb 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -76,7 +76,7 @@ void EntityTreeRenderer::init() { // make sure our "last avatar position" is something other than our current position, so that on our // first chance, we'll check for enter/leave entity events. glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition(); - _lastAvatarPosition = avatarPosition + glm::vec3(1.f,1.f,1.f); + _lastAvatarPosition = avatarPosition + glm::vec3(1.f, 1.f, 1.f); } QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {