diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index a90960a330..576c4335f6 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -15,24 +15,64 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// FIXME kickback functionality was removed because the joint setting interface in +// MyAvatar has apparently changed, breaking it. + +Script.include("../../libraries/utils.js"); +Script.include("../../libraries/constants.js"); +Script.include("../../libraries/toolBars.js"); + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -var RED = { red: 255, green: 0, blue: 0 }; var LASER_WIDTH = 2; +var POSE_CONTROLS = [ Controller.Standard.LeftHand, Controller.Standard.RightHand ]; +var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT ]; +var MIN_THROWER_DELAY = 1000; +var MAX_THROWER_DELAY = 1000; +var RELOAD_INTERVAL = 5; +var GUN_MODEL = HIFI_PUBLIC_BUCKET + "cozza13/gun/m1911-handgun+1.fbx?v=4"; +var BULLET_VELOCITY = 10.0; +var GUN_OFFSETS = [ { + x: -0.04, + y: 0.26, + z: 0.04 +}, { + x: 0.04, + y: 0.26, + z: 0.04 +} ]; +var GUN_ORIENTATIONS = [ Quat.fromPitchYawRollDegrees(0, 90, 90), Quat.fromPitchYawRollDegrees(0, -90, 270) ]; + +var BARREL_OFFSETS = [ { + x: -0.12, + y: 0.12, + z: 0.04 +}, { + x: 0.12, + y: 0.12, + z: 0.04 +} ]; + +var mapping = Controller.newMapping(); +var validPoses = [ false, false ]; +var barrelVectors = [ 0, 0 ]; +var barrelTips = [ 0, 0 ]; var pointer = []; + pointer.push(Overlays.addOverlay("line3d", { - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: RED, + start: ZERO_VECTOR, + end: ZERO_VECTOR, + color: COLORS.RED, alpha: 1, visible: true, lineWidth: LASER_WIDTH })); + pointer.push(Overlays.addOverlay("line3d", { - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: RED, + start: ZERO_VECTOR, + end: ZERO_VECTOR, + color: COLORS.RED, alpha: 1, visible: true, lineWidth: LASER_WIDTH @@ -42,135 +82,116 @@ function getRandomFloat(min, max) { return Math.random() * (max - min) + min; } -var lastX = 0; -var lastY = 0; -var yawFromMouse = 0; -var pitchFromMouse = 0; -var isMouseDown = false; - -var MIN_THROWER_DELAY = 1000; -var MAX_THROWER_DELAY = 1000; -var LEFT_BUTTON_3 = 3; -var RELOAD_INTERVAL = 5; - -var KICKBACK_ANGLE = 15; -var elbowKickAngle = 0.0; -var rotationBeforeKickback; - var showScore = false; - - -// Load some sound to use for loading and firing +// Load some sound to use for loading and firing var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw"); var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw"); var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); -var gunModel = "https://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx?v=4"; - var audioOptions = { - volume: 0.9 + volume: 0.9 } var shotsFired = 0; -var shotTime = new Date(); - -var activeControllers = 0; - -// initialize our controller triggers -var triggerPulled = new Array(); -var numberOfTriggers = Controller.getNumberOfTriggers(); -for (t = 0; t < numberOfTriggers; t++) { - triggerPulled[t] = false; -} - -var isLaunchButtonPressed = false; -var score = 0; +var shotTime = new Date(); +var isLaunchButtonPressed = false; +var score = 0; var bulletID = false; var targetID = false; -// Create overlay buttons and reticle - +// Create overlay buttons and reticle var BUTTON_SIZE = 32; var PADDING = 3; var NUM_BUTTONS = 3; - var screenSize = Controller.getViewportDimensions(); var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; -Script.include(["../../libraries/toolBars.js"]); -var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) { +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function(screenSize) { return { x: startX, y: (screenSize.y - (BUTTON_SIZE + PADDING)), }; }); -var reticle = Overlays.addOverlay("image", { - x: screenSize.x / 2 - (BUTTON_SIZE / 2), - y: screenSize.y / 2 - (BUTTON_SIZE / 2), - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: HIFI_PUBLIC_BUCKET + "images/gun/crosshairs.svg", - alpha: 1 - }); var offButton = toolBar.addOverlay("image", { - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", - alpha: 1 - }); + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", + alpha: 1 +}); startX += BUTTON_SIZE + PADDING; var platformButton = toolBar.addOverlay("image", { - x: startX, - y: screenSize.y - (BUTTON_SIZE + PADDING), - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg", - alpha: 1 - }); + x: startX, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg", + alpha: 1 +}); startX += BUTTON_SIZE + PADDING; var gridButton = toolBar.addOverlay("image", { - x: startX, - y: screenSize.y - (BUTTON_SIZE + PADDING), - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg", - alpha: 1 - }); + x: startX, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg", + alpha: 1 +}); if (showScore) { var text = Overlays.addOverlay("text", { - x: screenSize.x / 2 - 100, - y: screenSize.y / 2 - 50, - width: 150, - height: 50, - color: { red: 0, green: 0, blue: 0}, - textColor: { red: 255, green: 0, blue: 0}, - topMargin: 4, - leftMargin: 4, - text: "Score: " + score - }); + x: screenSize.x / 2 - 100, + y: screenSize.y / 2 - 50, + width: 150, + height: 50, + color: { + red: 0, + green: 0, + blue: 0 + }, + textColor: { + red: 255, + green: 0, + blue: 0 + }, + topMargin: 4, + leftMargin: 4, + text: "Score: " + score + }); } -var BULLET_VELOCITY = 10.0; - function entityCollisionWithEntity(entity1, entity2, collision) { if (entity2 === targetID) { score++; if (showScore) { - Overlays.editOverlay(text, { text: "Score: " + score } ); + Overlays.editOverlay(text, { + text: "Score: " + score + }); } - // We will delete the bullet and target in 1/2 sec, but for now we can see them bounce! + // We will delete the bullet and target in 1/2 sec, but for now we can + // see them bounce! Script.setTimeout(deleteBulletAndTarget, 500); // Turn the target and the bullet white - Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }}); - Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }}); + Entities.editEntity(entity1, { + color: { + red: 255, + green: 255, + blue: 255 + } + }); + Entities.editEntity(entity2, { + color: { + red: 255, + green: 255, + blue: 255 + } + }); // play the sound near the camera so the shooter can hear it audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); @@ -188,41 +209,45 @@ function shootBullet(position, velocity, grenade) { var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity; var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE; - var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; + var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; + + bulletID = Entities.addEntity({ + type: "Sphere", + position: position, + dimensions: { + x: bSize, + y: bSize, + z: bSize + }, + color: { + red: 0, + green: 0, + blue: 0 + }, + velocity: bVelocity, + lifetime: BULLET_LIFETIME, + gravity: { + x: 0, + y: bGravity, + z: 0 + }, + damping: 0.01, + density: 8000, + ignoreCollisions: false, + collisionsWillMove: true + }); - bulletID = Entities.addEntity( - { type: "Sphere", - position: position, - dimensions: { x: bSize, y: bSize, z: bSize }, - color: { red: 0, green: 0, blue: 0 }, - velocity: bVelocity, - lifetime: BULLET_LIFETIME, - gravity: { x: 0, y: bGravity, z: 0 }, - damping: 0.01, - density: 8000, - ignoreCollisions: false, - collisionsWillMove: true - }); Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity); - // Play firing sounds - audioOptions.position = position; + // Play firing sounds + audioOptions.position = position; Audio.playSound(fireSound, audioOptions); shotsFired++; if ((shotsFired % RELOAD_INTERVAL) == 0) { Audio.playSound(loadSound, audioOptions); } - - // Kickback the arm - if (elbowKickAngle > 0.0) { - MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback); - } - rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm"); - var armRotation = MyAvatar.getJointRotation("LeftForeArm"); - armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE)); - MyAvatar.setJointData("LeftForeArm", armRotation); - elbowKickAngle = KICKBACK_ANGLE; } + function shootTarget() { var TARGET_SIZE = 0.50; var TARGET_GRAVITY = 0.0; @@ -232,95 +257,152 @@ function shootTarget() { var DISTANCE_TO_LAUNCH_FROM = 5.0; var ANGLE_RANGE_FOR_LAUNCH = 20.0; var camera = Camera.getPosition(); - - var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 }); + + var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { + x: 0, + y: 1, + z: 0 + }); targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); var forwardVector = Quat.getFront(targetDirection); - + var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM)); var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY); velocity.y += TARGET_UP_VELOCITY; - targetID = Entities.addEntity( - { type: "Box", - position: newPosition, - dimensions: { x: TARGET_SIZE * (0.5 + Math.random()), y: TARGET_SIZE * (0.5 + Math.random()), z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 }, - color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, - velocity: velocity, - gravity: { x: 0, y: TARGET_GRAVITY, z: 0 }, - lifetime: TARGET_LIFETIME, - rotation: Camera.getOrientation(), - damping: 0.1, - density: 100.0, - collisionsWillMove: true }); + targetID = Entities.addEntity({ + type: "Box", + position: newPosition, + dimensions: { + x: TARGET_SIZE * (0.5 + Math.random()), + y: TARGET_SIZE * (0.5 + Math.random()), + z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 + }, + color: { + red: Math.random() * 255, + green: Math.random() * 255, + blue: Math.random() * 255 + }, + velocity: velocity, + gravity: { + x: 0, + y: TARGET_GRAVITY, + z: 0 + }, + lifetime: TARGET_LIFETIME, + rotation: Camera.getOrientation(), + damping: 0.1, + density: 100.0, + collisionsWillMove: true + }); - // Record start time + // Record start time shotTime = new Date(); // Play target shoot sound - audioOptions.position = newPosition; + audioOptions.position = newPosition; Audio.playSound(targetLaunchSound, audioOptions); } function makeGrid(type, scale, size) { - var separation = scale * 2; + var separation = scale * 2; var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); var x, y, z; - var GRID_LIFE = 60.0; - var dimensions; + var GRID_LIFE = 60.0; + var dimensions; for (x = 0; x < size; x++) { for (y = 0; y < size; y++) { for (z = 0; z < size; z++) { - - dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 }; - Entities.addEntity( - { type: type, - position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation }, - dimensions: dimensions, - color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, - velocity: { x: 0, y: 0, z: 0 }, - gravity: { x: 0, y: 0, z: 0 }, - lifetime: GRID_LIFE, - rotation: Camera.getOrientation(), - damping: 0.1, - density: 100.0, - collisionsWillMove: true }); + dimensions = { + x: separation / 2.0 * (0.5 + Math.random()), + y: separation / 2.0 * (0.5 + Math.random()), + z: separation / 2.0 * (0.5 + Math.random()) / 4.0 + }; + + Entities.addEntity({ + type: type, + position: { + x: pos.x + x * separation, + y: pos.y + y * separation, + z: pos.z + z * separation + }, + dimensions: dimensions, + color: { + red: Math.random() * 255, + green: Math.random() * 255, + blue: Math.random() * 255 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + gravity: { + x: 0, + y: 0, + z: 0 + }, + lifetime: GRID_LIFE, + rotation: Camera.getOrientation(), + damping: 0.1, + density: 100.0, + collisionsWillMove: true + }); } } } } function makePlatform(gravity, scale, size) { - var separation = scale * 2; + var separation = scale * 2; var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); pos.y -= separation * size; var x, y, z; - var TARGET_LIFE = 60.0; + var TARGET_LIFE = 60.0; var INITIAL_GAP = 0.5; - var dimensions; + var dimensions; for (x = 0; x < size; x++) { for (y = 0; y < size; y++) { for (z = 0; z < size; z++) { - dimensions = { x: separation/2.0, y: separation, z: separation/2.0 }; + dimensions = { + x: separation / 2.0, + y: separation, + z: separation / 2.0 + }; - Entities.addEntity( - { type: "Box", - position: { x: pos.x - (separation * size / 2.0) + x * separation, - y: pos.y + y * (separation + INITIAL_GAP), - z: pos.z - (separation * size / 2.0) + z * separation }, - dimensions: dimensions, - color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, - velocity: { x: 0, y: 0.05, z: 0 }, - gravity: { x: 0, y: gravity, z: 0 }, - lifetime: TARGET_LIFE, - damping: 0.1, - density: 100.0, - collisionsWillMove: true }); + Entities.addEntity({ + type: "Box", + position: { + x: pos.x - (separation * size / 2.0) + x * separation, + y: pos.y + y * (separation + INITIAL_GAP), + z: pos.z - (separation * size / 2.0) + z * separation + }, + dimensions: dimensions, + color: { + red: Math.random() * 255, + green: Math.random() * 255, + blue: Math.random() * 255 + }, + velocity: { + x: 0, + y: 0.05, + z: 0 + }, + gravity: { + x: 0, + y: gravity, + z: 0 + }, + lifetime: TARGET_LIFE, + damping: 0.1, + density: 100.0, + collisionsWillMove: true + }); } } } @@ -328,9 +410,21 @@ function makePlatform(gravity, scale, size) { // Make a floor for this stuff to fall onto Entities.addEntity({ type: "Box", - position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, - dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size }, - color: { red: 100, green: 100, blue: 100 }, + position: { + x: pos.x, + y: pos.y - separation / 2.0, + z: pos.z + }, + dimensions: { + x: 2.0 * separation * size, + y: separation / 2.0, + z: 2.0 * separation * size + }, + color: { + red: 100, + green: 100, + blue: 100 + }, lifetime: TARGET_LIFE }); @@ -340,153 +434,79 @@ function keyPressEvent(event) { // if our tools are off, then don't do anything if (event.text == "t") { var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; - Script.setTimeout(shootTarget, time); + Script.setTimeout(shootTarget, time); } else if ((event.text == ".") || (event.text == "SPACE")) { shootFromMouse(false); } else if (event.text == ",") { shootFromMouse(true); } else if (event.text == "r") { playLoadSound(); - } else if (event.text == "s") { - // Hit this key to dump a posture from hydra to log - Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm")); - Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm")); - Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand")); } } function playLoadSound() { - audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + audioOptions.position = MyAvatar.leftHandPose.translation; Audio.playSound(loadSound, audioOptions); - // Raise arm to firing posture - takeFiringPose(); -} - -function clearPose() { - MyAvatar.clearJointData("LeftForeArm"); - MyAvatar.clearJointData("LeftArm"); - MyAvatar.clearJointData("LeftHand"); } function deleteBulletAndTarget() { Entities.deleteEntity(bulletID); Entities.deleteEntity(targetID); - bulletID = false; - targetID = false; + bulletID = false; + targetID = false; } -function takeFiringPose() { - clearPose(); - if (Controller.getNumberOfSpatialControls() == 0) { - MyAvatar.setJointData("LeftForeArm", {x: -0.251919, y: -0.0415449, z: 0.499487, w: 0.827843}); - MyAvatar.setJointData("LeftArm", { x: 0.470196, y: -0.132559, z: 0.494033, w: 0.719219}); - MyAvatar.setJointData("LeftHand", { x: -0.0104815, y: -0.110551, z: -0.352111, w: 0.929333}); - } -} - -MyAvatar.attach(gunModel, "RightHand", {x:0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, -85, 79), 0.40); -MyAvatar.attach(gunModel, "LeftHand", {x:-0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, 85, -79), 0.40); - -// Give a bit of time to load before playing sound -Script.setTimeout(playLoadSound, 2000); - function update(deltaTime) { - if (activeControllers == 0) { - if (Controller.getNumberOfSpatialControls() > 0) { - activeControllers = Controller.getNumberOfSpatialControls(); - clearPose(); - } - } + // FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2] + var tipPoses = [ MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose ]; - var KICKBACK_DECAY_RATE = 0.125; - if (elbowKickAngle > 0.0) { - if (elbowKickAngle > 0.5) { - var newAngle = elbowKickAngle * KICKBACK_DECAY_RATE; - elbowKickAngle -= newAngle; - var armRotation = MyAvatar.getJointRotation("LeftForeArm"); - armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, -newAngle)); - MyAvatar.setJointData("LeftForeArm", armRotation); - } else { - MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback); - if (Controller.getNumberOfSpatialControls() > 0) { - clearPose(); - } - elbowKickAngle = 0.0; - } - } - - - // check for trigger press - - var numberOfTriggers = 2; - var controllersPerTrigger = 2; - - if (numberOfTriggers == 2 && controllersPerTrigger == 2) { - for (var t = 0; t < 2; t++) { - var shootABullet = false; - var triggerValue = Controller.getTriggerValue(t); - if (triggerPulled[t]) { - // must release to at least 0.1 - if (triggerValue < 0.1) { - triggerPulled[t] = false; // unpulled - } - } else { - // must pull to at least - if (triggerValue > 0.5) { - triggerPulled[t] = true; // pulled - shootABullet = true; - } - } - var palmController = t * controllersPerTrigger; - var palmPosition = Controller.getSpatialControlPosition(palmController); - var fingerTipController = palmController + 1; - var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController); - var laserTip = Vec3.sum(Vec3.multiply(100.0, Vec3.subtract(fingerTipPosition, palmPosition)), palmPosition); - - // Update Lasers - Overlays.editOverlay(pointer[t], { - start: palmPosition, - end: laserTip, - alpha: 1 + for (var side = 0; side < 2; side++) { + // First check if the controller is valid + var controllerPose = Controller.getPoseValue(POSE_CONTROLS[side]); + validPoses[side] = controllerPose.valid; + if (!controllerPose.valid) { + Overlays.editOverlay(pointer[side], { + visible: false }); - - if (shootABullet) { - - var palmToFingerTipVector = - { x: (fingerTipPosition.x - palmPosition.x), - y: (fingerTipPosition.y - palmPosition.y), - z: (fingerTipPosition.z - palmPosition.z) }; - - // just off the front of the finger tip - var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2, - y: fingerTipPosition.y + palmToFingerTipVector.y/2, - z: fingerTipPosition.z + palmToFingerTipVector.z/2}; - - var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector)); - - shootBullet(position, velocity, false); - } + continue; } + + // Need to adjust the laser + var tipPose = tipPoses[side]; + var handRotation = tipPoses[side].rotation; + var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]); + barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset); + barrelVectors[side] = Vec3.multiplyQbyV(handRotation, { + x: 0, + y: 1, + z: 0 + }); + + var laserTip = Vec3.sum(Vec3.multiply(100.0, barrelVectors[side]), barrelTips[side]); + // Update Lasers + Overlays.editOverlay(pointer[side], { + start: barrelTips[side], + end: laserTip, + alpha: 1, + visible: true + }); } } -function shootFromMouse(grenade) { - var DISTANCE_FROM_CAMERA = 1.0; - var camera = Camera.getPosition(); - var forwardVector = Quat.getFront(Camera.getOrientation()); - var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); - var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); - shootBullet(newPosition, velocity, grenade); -} - -function mouseReleaseEvent(event) { - // position - isMouseDown = false; +function triggerChanged(side, value) { + var pressed = (value != 0); + if (pressed) { + var position = barrelTips[side]; + var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(barrelVectors[side])); + shootBullet(position, velocity, false); + } } function mousePressEvent(event) { - var clickedText = false; - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (clickedOverlay == offButton) { Script.stop(); } else if (clickedOverlay == platformButton) { @@ -494,25 +514,37 @@ function mousePressEvent(event) { makePlatform(-9.8, 1.0, platformSize); } else if (clickedOverlay == gridButton) { makeGrid("Box", 1.0, 3); - } + } } function scriptEnding() { - Overlays.deleteOverlay(reticle); + mapping.disable(); toolBar.cleanup(); - Overlays.deleteOverlay(pointer[0]); - Overlays.deleteOverlay(pointer[1]); + for (var i = 0; i < pointer.length; ++i) { + Overlays.deleteOverlay(pointer[i]); + } Overlays.deleteOverlay(text); - MyAvatar.detachOne(gunModel); - MyAvatar.detachOne(gunModel); + MyAvatar.detachOne(GUN_MODEL); + MyAvatar.detachOne(GUN_MODEL); clearPose(); } +MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[0], 0.40); +MyAvatar.attach(GUN_MODEL, "RightHand", GUN_OFFSETS[1], GUN_ORIENTATIONS[1], 0.40); + +// Give a bit of time to load before playing sound +Script.setTimeout(playLoadSound, 2000); + +mapping.from(Controller.Standard.LT).hysteresis(0.1, 0.5).to(function(value) { + triggerChanged(0, value); +}); + +mapping.from(Controller.Standard.RT).hysteresis(0.1, 0.5).to(function(value) { + triggerChanged(1, value); +}); +mapping.enable(); + Script.scriptEnding.connect(scriptEnding); Script.update.connect(update); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.keyPressEvent.connect(keyPressEvent); - - - diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index bc31e9ea44..09188318eb 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -14,101 +14,183 @@ #include #include -#include "SharedUtil.h" +#include + +#include "filters/ClampFilter.h" +#include "filters/ConstrainToIntegerFilter.h" +#include "filters/ConstrainToPositiveIntegerFilter.h" +#include "filters/DeadZoneFilter.h" +#include "filters/HysteresisFilter.h" +#include "filters/InvertFilter.h" +#include "filters/PulseFilter.h" +#include "filters/ScaleFilter.h" using namespace controller; Filter::Factory Filter::_factory; -REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") +REGISTER_FILTER_CLASS_INSTANCE(ClampFilter, "clamp") REGISTER_FILTER_CLASS_INSTANCE(ConstrainToIntegerFilter, "constrainToInteger") REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger") -REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") -REGISTER_FILTER_CLASS_INSTANCE(ClampFilter, "clamp") REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone") +REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis") +REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") +REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") - const QString JSON_FILTER_TYPE = QStringLiteral("type"); const QString JSON_FILTER_PARAMS = QStringLiteral("params"); -Filter::Pointer Filter::parse(const QJsonObject& json) { - // The filter is an object, now let s check for type and potential arguments +Filter::Pointer Filter::parse(const QJsonValue& json) { Filter::Pointer filter; - auto filterType = json[JSON_FILTER_TYPE]; - if (filterType.isString()) { + if (json.isString()) { + filter = Filter::getFactory().create(json.toString()); + } else if (json.isObject()) { + QJsonObject jsonObj = json.toObject(); + // The filter is an object, now let s check for type and potential arguments + auto filterType = jsonObj[JSON_FILTER_TYPE]; filter = Filter::getFactory().create(filterType.toString()); if (filter) { - // Filter is defined, need to read the parameters and validate - auto parameters = json[JSON_FILTER_PARAMS]; - if (parameters.isArray()) { - if (filter->parseParameters(parameters.toArray())) { - } + QJsonValue params = jsonObj; + if (jsonObj.contains(JSON_FILTER_PARAMS)) { + params = jsonObj[JSON_FILTER_PARAMS]; + } + if (!filter->parseParameters(params)) { + qWarning() << "Unable to parse filter parameters " << params; + return Filter::Pointer(); } - - return filter; } } - return Filter::Pointer(); + return filter; } - -bool ScaleFilter::parseParameters(const QJsonArray& parameters) { - if (parameters.size() > 1) { - _scale = parameters[0].toDouble(); - } - return true; -} - - -bool ClampFilter::parseParameters(const QJsonArray& parameters) { - if (parameters.size() > 1) { - _min = parameters[0].toDouble(); - } - if (parameters.size() > 2) { - _max = parameters[1].toDouble(); - } - return true; -} - - -float DeadZoneFilter::apply(float value) const { - float scale = 1.0f / (1.0f - _min); - if (std::abs(value) < _min) { - return 0.0f; - } - return (value - _min) * scale; -} - -bool DeadZoneFilter::parseParameters(const QJsonArray& parameters) { - if (parameters.size() > 1) { - _min = parameters[0].toDouble(); - } - return true; -} - - -float PulseFilter::apply(float value) const { - float result = 0.0f; - - if (0.0f != value) { - float now = secTimestampNow(); - float delta = now - _lastEmitTime; - if (delta >= _interval) { - _lastEmitTime = now; - result = value; +bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output) { + if (parameters.isDouble()) { + output = parameters.toDouble(); + return true; + } else if (parameters.isArray()) { + auto arrayParameters = parameters.toArray(); + if (arrayParameters.size() > 1) { + output = arrayParameters[0].toDouble(); + return true; } - } - - return result; -} - -bool PulseFilter::parseParameters(const QJsonArray& parameters) { - if (parameters.size() > 1) { - _interval = parameters[0].toDouble(); - } - return true; + } else if (parameters.isObject()) { + static const QString JSON_MIN = QStringLiteral("interval"); + auto objectParameters = parameters.toObject(); + if (objectParameters.contains(name)) { + output = objectParameters[name].toDouble(); + return true; + } + } + return false; } + +#if 0 + +namespace controller { + + class LambdaFilter : public Filter { + public: + // LambdaFilter() {}12 + LambdaFilter(Lambda f) : _function(f) {}; + + virtual float apply(float value) const { + return _function(value); + } + + virtual bool parseParameters(const QJsonArray& parameters) { return true; } + + // REGISTER_FILTER_CLASS(LambdaFilter); + private: + Lambda _function; + }; + + class ScriptFilter : public Filter { + public: + + }; + + + + //class EasingFilter : public Filter { + //public: + // virtual float apply(float value) const override; + + //private: + // QEasingCurve _curve; + //}; + + //// GLSL style filters + //class StepFilter : public Filter { + //public: + // StepFilter(float edge) : _edge(edge) {}; + // virtual float apply(float value) const override; + + //private: + // const float _edge; + //}; + + //class PowFilter : public Filter { + //public: + // PowFilter(float exponent) : _exponent(exponent) {}; + // virtual float apply(float value) const override; + + //private: + // const float _exponent; + //}; + + //class AbsFilter : public Filter { + //public: + // virtual float apply(float value) const override; + //}; + + //class SignFilter : public Filter { + //public: + // virtual float apply(float value) const override; + //}; + + //class FloorFilter : public Filter { + //public: + // virtual float apply(float value) const override { + // return floor(newValue); + // } + //}; + + //class CeilFilter : public Filter { + //public: + // virtual float apply(float value) const override { + // return ceil(newValue); + // } + //}; + + //class FractFilter : public Filter { + //public: + // virtual float apply(float value) const override { + // return fract(newValue); + // } + //}; + + //class MinFilter : public Filter { + //public: + // MinFilter(float mine) : _min(min) {}; + + // virtual float apply(float value) const override { + // return glm::min(_min, newValue); + // } + + //private: + // const float _min; + //}; + + //class MaxFilter : public Filter { + //public: + // MaxFilter(float max) : _max(max) {}; + // virtual float apply(float newValue, float oldValue) override; + //private: + // const float _max; + //}; +} +#endif \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h index 1fa9833044..77585c8ebb 100644 --- a/libraries/controllers/src/controllers/impl/Filter.h +++ b/libraries/controllers/src/controllers/impl/Filter.h @@ -21,8 +21,7 @@ #include -class QJsonObject; -class QJsonArray; +class QJsonValue; namespace controller { @@ -36,11 +35,13 @@ namespace controller { virtual float apply(float value) const = 0; // Factory features - virtual bool parseParameters(const QJsonArray& parameters) { return true; } + virtual bool parseParameters(const QJsonValue& parameters) { return true; } - static Pointer parse(const QJsonObject& json); + static Pointer parse(const QJsonValue& json); static void registerBuilder(const QString& name, Factory::Builder builder); static Factory& getFactory() { return _factory; } + + static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output); protected: static Factory _factory; }; @@ -54,194 +55,5 @@ namespace controller { #define REGISTER_FILTER_CLASS_INSTANCE(classEntry, className) \ classEntry::Registrar classEntry::_registrar(className, Filter::getFactory()); -namespace controller { - - class LambdaFilter : public Filter { - public: - // LambdaFilter() {} - LambdaFilter(Lambda f) : _function(f) {}; - - virtual float apply(float value) const { - return _function(value); - } - - virtual bool parseParameters(const QJsonArray& parameters) { return true; } - -// REGISTER_FILTER_CLASS(LambdaFilter); - private: - Lambda _function; - }; - - class ScriptFilter : public Filter { - public: - - }; - - class ScaleFilter : public Filter { - REGISTER_FILTER_CLASS(ScaleFilter); - public: - ScaleFilter() {} - ScaleFilter(float scale): _scale(scale) {} - - virtual float apply(float value) const override { - return value * _scale; - } - virtual bool parseParameters(const QJsonArray& parameters); - - private: - float _scale = 1.0f; - }; - - class InvertFilter : public ScaleFilter { - REGISTER_FILTER_CLASS(InvertFilter); - public: - InvertFilter() : ScaleFilter(-1.0f) {} - - virtual bool parseParameters(const QJsonArray& parameters) { return true; } - - private: - }; - - class ClampFilter : public Filter { - REGISTER_FILTER_CLASS(ClampFilter); - public: - ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {}; - - virtual float apply(float value) const override { - return glm::clamp(value, _min, _max); - } - virtual bool parseParameters(const QJsonArray& parameters) override; - protected: - float _min = 0.0f; - float _max = 1.0f; - }; - - class DeadZoneFilter : public Filter { - REGISTER_FILTER_CLASS(DeadZoneFilter); - public: - DeadZoneFilter(float min = 0.0) : _min(min) {}; - - virtual float apply(float value) const override; - virtual bool parseParameters(const QJsonArray& parameters) override; - protected: - float _min = 0.0f; - }; - - class PulseFilter : public Filter { - REGISTER_FILTER_CLASS(PulseFilter); - public: - PulseFilter() {} - PulseFilter(float interval) : _interval(interval) {} - - - virtual float apply(float value) const override; - - virtual bool parseParameters(const QJsonArray& parameters); - - private: - mutable float _lastEmitTime{ -::std::numeric_limits::max() }; - float _interval = 1.0f; - }; - - class ConstrainToIntegerFilter : public Filter { - REGISTER_FILTER_CLASS(ConstrainToIntegerFilter); - public: - ConstrainToIntegerFilter() {}; - - virtual float apply(float value) const override { - return glm::sign(value); - } - protected: - }; - - class ConstrainToPositiveIntegerFilter : public Filter { - REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter); - public: - ConstrainToPositiveIntegerFilter() {}; - - virtual float apply(float value) const override { - return (value <= 0.0f) ? 0.0f : 1.0f; - } - protected: - }; - - //class EasingFilter : public Filter { - //public: - // virtual float apply(float value) const override; - - //private: - // QEasingCurve _curve; - //}; - - //// GLSL style filters - //class StepFilter : public Filter { - //public: - // StepFilter(float edge) : _edge(edge) {}; - // virtual float apply(float value) const override; - - //private: - // const float _edge; - //}; - - //class PowFilter : public Filter { - //public: - // PowFilter(float exponent) : _exponent(exponent) {}; - // virtual float apply(float value) const override; - - //private: - // const float _exponent; - //}; - - //class AbsFilter : public Filter { - //public: - // virtual float apply(float value) const override; - //}; - - //class SignFilter : public Filter { - //public: - // virtual float apply(float value) const override; - //}; - - //class FloorFilter : public Filter { - //public: - // virtual float apply(float value) const override { - // return floor(newValue); - // } - //}; - - //class CeilFilter : public Filter { - //public: - // virtual float apply(float value) const override { - // return ceil(newValue); - // } - //}; - - //class FractFilter : public Filter { - //public: - // virtual float apply(float value) const override { - // return fract(newValue); - // } - //}; - - //class MinFilter : public Filter { - //public: - // MinFilter(float mine) : _min(min) {}; - - // virtual float apply(float value) const override { - // return glm::min(_min, newValue); - // } - - //private: - // const float _min; - //}; - - //class MaxFilter : public Filter { - //public: - // MaxFilter(float max) : _max(max) {}; - // virtual float apply(float newValue, float oldValue) override; - //private: - // const float _max; - //}; -} #endif diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index d56d699c28..49e615439d 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -18,6 +18,15 @@ #include "../ScriptingInterface.h" #include "../Logging.h" +#include "filters/ClampFilter.h" +#include "filters/ConstrainToIntegerFilter.h" +#include "filters/ConstrainToPositiveIntegerFilter.h" +#include "filters/DeadZoneFilter.h" +#include "filters/HysteresisFilter.h" +#include "filters/InvertFilter.h" +#include "filters/PulseFilter.h" +#include "filters/ScaleFilter.h" + using namespace controller; void RouteBuilderProxy::toQml(const QJSValue& destination) { @@ -43,18 +52,6 @@ QObject* RouteBuilderProxy::debug(bool enable) { return this; } -QObject* RouteBuilderProxy::filterQml(const QJSValue& expression) { - if (expression.isCallable()) { - addFilter([=](float value) { - QJSValue originalExpression = expression; - QJSValueList params({ QJSValue(value) }); - auto result = originalExpression.call(params); - return (float)(result.toNumber()); - }); - } - return this; -} - QObject* RouteBuilderProxy::when(const QScriptValue& expression) { _route->conditional = _parent.conditionalFor(expression); return this; @@ -65,53 +62,46 @@ QObject* RouteBuilderProxy::whenQml(const QJSValue& expression) { return this; } - -QObject* RouteBuilderProxy::filter(const QScriptValue& expression) { - return this; -} - - QObject* RouteBuilderProxy::clamp(float min, float max) { - addFilter(Filter::Pointer(new ClampFilter(min, max))); + addFilter(std::make_shared(min, max)); return this; } QObject* RouteBuilderProxy::scale(float multiplier) { - addFilter(Filter::Pointer(new ScaleFilter(multiplier))); + addFilter(std::make_shared(multiplier)); return this; } QObject* RouteBuilderProxy::invert() { - addFilter(Filter::Pointer(new InvertFilter())); + addFilter(std::make_shared()); + return this; +} + +QObject* RouteBuilderProxy::hysteresis(float min, float max) { + addFilter(std::make_shared(min, max)); return this; } QObject* RouteBuilderProxy::deadZone(float min) { - addFilter(Filter::Pointer(new DeadZoneFilter(min))); + addFilter(std::make_shared(min)); return this; } QObject* RouteBuilderProxy::constrainToInteger() { - addFilter(Filter::Pointer(new ConstrainToIntegerFilter())); + addFilter(std::make_shared()); return this; } QObject* RouteBuilderProxy::constrainToPositiveInteger() { - addFilter(Filter::Pointer(new ConstrainToPositiveIntegerFilter())); + addFilter(std::make_shared()); return this; } - QObject* RouteBuilderProxy::pulse(float interval) { - addFilter(Filter::Pointer(new PulseFilter(interval))); + addFilter(std::make_shared(interval)); return this; } -void RouteBuilderProxy::addFilter(Filter::Lambda lambda) { - Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda); - addFilter(filterPointer); -} - void RouteBuilderProxy::addFilter(Filter::Pointer filter) { _route->filters.push_back(filter); } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 4bcfba5acd..d55aa80f6b 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -34,14 +34,13 @@ class RouteBuilderProxy : public QObject { : _parent(parent), _mapping(mapping), _route(route) { } Q_INVOKABLE void toQml(const QJSValue& destination); - Q_INVOKABLE QObject* filterQml(const QJSValue& expression); Q_INVOKABLE QObject* whenQml(const QJSValue& expression); Q_INVOKABLE void to(const QScriptValue& destination); Q_INVOKABLE QObject* debug(bool enable = true); Q_INVOKABLE QObject* when(const QScriptValue& expression); - Q_INVOKABLE QObject* filter(const QScriptValue& expression); Q_INVOKABLE QObject* clamp(float min, float max); + Q_INVOKABLE QObject* hysteresis(float min, float max); Q_INVOKABLE QObject* pulse(float interval); Q_INVOKABLE QObject* scale(float multiplier); Q_INVOKABLE QObject* invert(); @@ -52,7 +51,6 @@ class RouteBuilderProxy : public QObject { private: void to(const Endpoint::Pointer& destination); void conditional(const Conditional::Pointer& conditional); - void addFilter(Filter::Lambda lambda); void addFilter(Filter::Pointer filter); UserInputMapper& _parent; Mapping::Pointer _mapping; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp index 3dedcef4e4..069bcb3c00 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -14,7 +14,7 @@ using namespace controller; float ScriptEndpoint::value() { updateValue(); - return _lastValue; + return _lastValueRead; } void ScriptEndpoint::updateValue() { @@ -23,14 +23,18 @@ void ScriptEndpoint::updateValue() { return; } - _lastValue = (float)_callable.call().toNumber(); + _lastValueRead = (float)_callable.call().toNumber(); } void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + if (newValue == _lastValueWritten) { + return; + } internalApply(newValue, oldValue, source->getInput().getID()); } void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { + _lastValueWritten = newValue; if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, Q_ARG(float, newValue), diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h index e3c7abe812..a56ac472be 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -31,7 +31,8 @@ protected: Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); private: QScriptValue _callable; - float _lastValue = 0.0f; + float _lastValueRead { 0.0f }; + float _lastValueWritten { 0.0f }; }; } diff --git a/libraries/controllers/src/controllers/impl/filters/ClampFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ClampFilter.cpp new file mode 100644 index 0000000000..ec22981ef3 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ClampFilter.cpp @@ -0,0 +1,38 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ClampFilter.h" + +#include +#include + +using namespace controller; + +bool ClampFilter::parseParameters(const QJsonValue& parameters) { + if (parameters.isArray()) { + auto arrayParameters = parameters.toArray(); + if (arrayParameters.size() > 1) { + _min = arrayParameters[0].toDouble(); + } + if (arrayParameters.size() > 2) { + _max = arrayParameters[1].toDouble(); + } + } else if (parameters.isObject()) { + static const QString JSON_MAX = QStringLiteral("max"); + static const QString JSON_MIN = QStringLiteral("min"); + + auto objectParameters = parameters.toObject(); + if (objectParameters.contains(JSON_MIN)) { + _min = objectParameters[JSON_MIN].toDouble(); + } + if (objectParameters.contains(JSON_MAX)) { + _max = objectParameters[JSON_MAX].toDouble(); + } + } + return true; +} diff --git a/libraries/controllers/src/controllers/impl/filters/ClampFilter.h b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h new file mode 100644 index 0000000000..fd82821b3e --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_Controllers_Filters_Clamp_h +#define hifi_Controllers_Filters_Clamp_h + +#include "../Filter.h" + +namespace controller { + +class ClampFilter : public Filter { + REGISTER_FILTER_CLASS(ClampFilter); +public: + ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {}; + virtual float apply(float value) const override { + return glm::clamp(value, _min, _max); + } + virtual bool parseParameters(const QJsonValue& parameters) override; +protected: + float _min = 0.0f; + float _max = 1.0f; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp new file mode 100644 index 0000000000..78ffb47693 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ConstrainToIntegerFilter.h" diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h new file mode 100644 index 0000000000..580dc2a856 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h @@ -0,0 +1,30 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_Controllers_Filters_ConstrainToIntegerFilter_h +#define hifi_Controllers_Filters_ConstrainToIntegerFilter_h + +#include "../Filter.h" + +namespace controller { + +class ConstrainToIntegerFilter : public Filter { + REGISTER_FILTER_CLASS(ConstrainToIntegerFilter); +public: + ConstrainToIntegerFilter() {}; + + virtual float apply(float value) const override { + return glm::sign(value); + } +protected: +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp new file mode 100644 index 0000000000..d78942b18f --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ConstrainToPositiveIntegerFilter.h" diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h new file mode 100644 index 0000000000..27395cde24 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h @@ -0,0 +1,30 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_Controllers_Filters_ConstrainToPositiveInteger_h +#define hifi_Controllers_Filters_ConstrainToPositiveInteger_h + +#include "../Filter.h" + +namespace controller { + +class ConstrainToPositiveIntegerFilter : public Filter { + REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter); +public: + ConstrainToPositiveIntegerFilter() {}; + + virtual float apply(float value) const override { + return (value <= 0.0f) ? 0.0f : 1.0f; + } +protected: +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.cpp b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.cpp new file mode 100644 index 0000000000..809308eeab --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.cpp @@ -0,0 +1,26 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DeadZoneFilter.h" + +#include +#include + +using namespace controller; +float DeadZoneFilter::apply(float value) const { + float scale = 1.0f / (1.0f - _min); + if (std::abs(value) < _min) { + return 0.0f; + } + return (value - _min) * scale; +} + +bool DeadZoneFilter::parseParameters(const QJsonValue& parameters) { + static const QString JSON_MIN = QStringLiteral("min"); + return parseSingleFloatParameter(parameters, JSON_MIN, _min); +} diff --git a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h new file mode 100644 index 0000000000..70ac657415 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h @@ -0,0 +1,31 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_Controllers_Filters_DeadZoneFilter_h +#define hifi_Controllers_Filters_DeadZoneFilter_h + +#include "../Filter.h" + +namespace controller { + +class DeadZoneFilter : public Filter { + REGISTER_FILTER_CLASS(DeadZoneFilter); +public: + DeadZoneFilter(float min = 0.0) : _min(min) {}; + + virtual float apply(float value) const override; + virtual bool parseParameters(const QJsonValue& parameters) override; +protected: + float _min = 0.0f; +}; + + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp new file mode 100644 index 0000000000..a7f22e1de4 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp @@ -0,0 +1,64 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "HysteresisFilter.h" + +#include +#include + +using namespace controller; + +HysteresisFilter::HysteresisFilter(float min, float max) : _min(min), _max(max) { + if (_min > _max) { + std::swap(_min, _max); + } +}; + + +float HysteresisFilter::apply(float value) const { + if (_signaled) { + if (value <= _min) { + _signaled = false; + } + } else { + if (value >= _max) { + _signaled = true; + } + } + return _signaled ? 1.0f : 0.0f; +} + +bool HysteresisFilter::parseParameters(const QJsonValue& parameters) { + if (parameters.isArray()) { + auto arrayParameters = parameters.toArray(); + if (arrayParameters.size() > 1) { + _min = arrayParameters[0].toDouble(); + } + if (arrayParameters.size() > 2) { + _max = arrayParameters[1].toDouble(); + } + } else if (parameters.isObject()) { + static const QString JSON_MAX = QStringLiteral("max"); + static const QString JSON_MIN = QStringLiteral("min"); + + auto objectParameters = parameters.toObject(); + if (objectParameters.contains(JSON_MIN)) { + _min = objectParameters[JSON_MIN].toDouble(); + } + if (objectParameters.contains(JSON_MAX)) { + _max = objectParameters[JSON_MAX].toDouble(); + } + } else { + return false; + } + + if (_min > _max) { + std::swap(_min, _max); + } + return true; +} diff --git a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h new file mode 100644 index 0000000000..4f7e07928d --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h @@ -0,0 +1,31 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_Controllers_Filters_Hysteresis_h +#define hifi_Controllers_Filters_Hysteresis_h + +#include "../Filter.h" + +namespace controller { + +class HysteresisFilter : public Filter { + REGISTER_FILTER_CLASS(HysteresisFilter); +public: + HysteresisFilter(float min = 0.25, float max = 0.75); + virtual float apply(float value) const override; + virtual bool parseParameters(const QJsonValue& parameters) override; +protected: + float _min; + float _max; + mutable bool _signaled { false }; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp b/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp new file mode 100644 index 0000000000..db582b84cc --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "InvertFilter.h" diff --git a/libraries/controllers/src/controllers/impl/filters/InvertFilter.h b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h new file mode 100644 index 0000000000..889cd0140c --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h @@ -0,0 +1,29 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_Controllers_Filters_InvertFilter_h +#define hifi_Controllers_Filters_InvertFilter_h + +#include "ScaleFilter.h" + +namespace controller { + +class InvertFilter : public ScaleFilter { + REGISTER_FILTER_CLASS(InvertFilter); +public: + InvertFilter() : ScaleFilter(-1.0f) {} + + virtual bool parseParameters(const QJsonArray& parameters) { return true; } + +private: +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp b/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp new file mode 100644 index 0000000000..f4e1f04791 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp @@ -0,0 +1,37 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "PulseFilter.h" + +#include +#include + +using namespace controller; + + + +float PulseFilter::apply(float value) const { + float result = 0.0f; + + if (0.0f != value) { + float now = secTimestampNow(); + float delta = now - _lastEmitTime; + if (delta >= _interval) { + _lastEmitTime = now; + result = value; + } + } + + return result; +} + +bool PulseFilter::parseParameters(const QJsonValue& parameters) { + static const QString JSON_MIN = QStringLiteral("interval"); + return parseSingleFloatParameter(parameters, JSON_MIN, _interval); +} + diff --git a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h new file mode 100644 index 0000000000..2512b479cf --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h @@ -0,0 +1,36 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_Controllers_Filters_Pulse_h +#define hifi_Controllers_Filters_Pulse_h + +#include "../Filter.h" + +namespace controller { + + +class PulseFilter : public Filter { + REGISTER_FILTER_CLASS(PulseFilter); +public: + PulseFilter() {} + PulseFilter(float interval) : _interval(interval) {} + + + virtual float apply(float value) const override; + + virtual bool parseParameters(const QJsonValue& parameters); + +private: + mutable float _lastEmitTime { -::std::numeric_limits::max() }; + float _interval = 1.0f; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.cpp new file mode 100644 index 0000000000..4a310e3a04 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.cpp @@ -0,0 +1,19 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ScaleFilter.h" + +#include +#include + +using namespace controller; + +bool ScaleFilter::parseParameters(const QJsonValue& parameters) { + static const QString JSON_SCALE = QStringLiteral("scale"); + return parseSingleFloatParameter(parameters, JSON_SCALE, _scale); +} diff --git a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h new file mode 100644 index 0000000000..39c5edd4e5 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h @@ -0,0 +1,34 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_Controllers_Filters_Scale_h +#define hifi_Controllers_Filters_Scale_h + +#include "../Filter.h" + +namespace controller { + +class ScaleFilter : public Filter { + REGISTER_FILTER_CLASS(ScaleFilter); +public: + ScaleFilter() {} + ScaleFilter(float scale) : _scale(scale) {} + + virtual float apply(float value) const override { + return value * _scale; + } + virtual bool parseParameters(const QJsonValue& parameters); + +private: + float _scale = 1.0f; +}; + +} + +#endif