mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-23 14:14:28 +02:00
Merge pull request #6177 from jherico/guns
Controller Branch - Fix gun.js
This commit is contained in:
commit
88ba6cda24
23 changed files with 983 additions and 600 deletions
|
@ -15,24 +15,64 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// 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/";
|
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||||
|
|
||||||
var RED = { red: 255, green: 0, blue: 0 };
|
|
||||||
var LASER_WIDTH = 2;
|
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 = [];
|
var pointer = [];
|
||||||
|
|
||||||
pointer.push(Overlays.addOverlay("line3d", {
|
pointer.push(Overlays.addOverlay("line3d", {
|
||||||
start: { x: 0, y: 0, z: 0 },
|
start: ZERO_VECTOR,
|
||||||
end: { x: 0, y: 0, z: 0 },
|
end: ZERO_VECTOR,
|
||||||
color: RED,
|
color: COLORS.RED,
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
visible: true,
|
visible: true,
|
||||||
lineWidth: LASER_WIDTH
|
lineWidth: LASER_WIDTH
|
||||||
}));
|
}));
|
||||||
|
|
||||||
pointer.push(Overlays.addOverlay("line3d", {
|
pointer.push(Overlays.addOverlay("line3d", {
|
||||||
start: { x: 0, y: 0, z: 0 },
|
start: ZERO_VECTOR,
|
||||||
end: { x: 0, y: 0, z: 0 },
|
end: ZERO_VECTOR,
|
||||||
color: RED,
|
color: COLORS.RED,
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
visible: true,
|
visible: true,
|
||||||
lineWidth: LASER_WIDTH
|
lineWidth: LASER_WIDTH
|
||||||
|
@ -42,24 +82,7 @@ function getRandomFloat(min, max) {
|
||||||
return Math.random() * (max - min) + min;
|
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;
|
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 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 loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw");
|
||||||
|
@ -67,24 +90,12 @@ var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletIm
|
||||||
var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.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 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 = {
|
var audioOptions = {
|
||||||
volume: 0.9
|
volume: 0.9
|
||||||
}
|
}
|
||||||
|
|
||||||
var shotsFired = 0;
|
var shotsFired = 0;
|
||||||
var shotTime = new Date();
|
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 isLaunchButtonPressed = false;
|
||||||
var score = 0;
|
var score = 0;
|
||||||
|
|
||||||
|
@ -92,35 +103,24 @@ var bulletID = false;
|
||||||
var targetID = false;
|
var targetID = false;
|
||||||
|
|
||||||
// Create overlay buttons and reticle
|
// Create overlay buttons and reticle
|
||||||
|
|
||||||
var BUTTON_SIZE = 32;
|
var BUTTON_SIZE = 32;
|
||||||
var PADDING = 3;
|
var PADDING = 3;
|
||||||
var NUM_BUTTONS = 3;
|
var NUM_BUTTONS = 3;
|
||||||
|
|
||||||
var screenSize = Controller.getViewportDimensions();
|
var screenSize = Controller.getViewportDimensions();
|
||||||
var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2;
|
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 {
|
return {
|
||||||
x: startX,
|
x: startX,
|
||||||
y: (screenSize.y - (BUTTON_SIZE + PADDING)),
|
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", {
|
var offButton = toolBar.addOverlay("image", {
|
||||||
width: BUTTON_SIZE,
|
width: BUTTON_SIZE,
|
||||||
height: BUTTON_SIZE,
|
height: BUTTON_SIZE,
|
||||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
|
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
|
||||||
alpha: 1
|
alpha: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
startX += BUTTON_SIZE + PADDING;
|
startX += BUTTON_SIZE + PADDING;
|
||||||
var platformButton = toolBar.addOverlay("image", {
|
var platformButton = toolBar.addOverlay("image", {
|
||||||
|
@ -130,7 +130,7 @@ var platformButton = toolBar.addOverlay("image", {
|
||||||
height: BUTTON_SIZE,
|
height: BUTTON_SIZE,
|
||||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",
|
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",
|
||||||
alpha: 1
|
alpha: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
startX += BUTTON_SIZE + PADDING;
|
startX += BUTTON_SIZE + PADDING;
|
||||||
var gridButton = toolBar.addOverlay("image", {
|
var gridButton = toolBar.addOverlay("image", {
|
||||||
|
@ -140,7 +140,7 @@ var gridButton = toolBar.addOverlay("image", {
|
||||||
height: BUTTON_SIZE,
|
height: BUTTON_SIZE,
|
||||||
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",
|
imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",
|
||||||
alpha: 1
|
alpha: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
if (showScore) {
|
if (showScore) {
|
||||||
var text = Overlays.addOverlay("text", {
|
var text = Overlays.addOverlay("text", {
|
||||||
|
@ -148,29 +148,50 @@ if (showScore) {
|
||||||
y: screenSize.y / 2 - 50,
|
y: screenSize.y / 2 - 50,
|
||||||
width: 150,
|
width: 150,
|
||||||
height: 50,
|
height: 50,
|
||||||
color: { red: 0, green: 0, blue: 0},
|
color: {
|
||||||
textColor: { red: 255, green: 0, blue: 0},
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 0
|
||||||
|
},
|
||||||
|
textColor: {
|
||||||
|
red: 255,
|
||||||
|
green: 0,
|
||||||
|
blue: 0
|
||||||
|
},
|
||||||
topMargin: 4,
|
topMargin: 4,
|
||||||
leftMargin: 4,
|
leftMargin: 4,
|
||||||
text: "Score: " + score
|
text: "Score: " + score
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var BULLET_VELOCITY = 10.0;
|
|
||||||
|
|
||||||
function entityCollisionWithEntity(entity1, entity2, collision) {
|
function entityCollisionWithEntity(entity1, entity2, collision) {
|
||||||
if (entity2 === targetID) {
|
if (entity2 === targetID) {
|
||||||
score++;
|
score++;
|
||||||
if (showScore) {
|
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);
|
Script.setTimeout(deleteBulletAndTarget, 500);
|
||||||
|
|
||||||
// Turn the target and the bullet white
|
// Turn the target and the bullet white
|
||||||
Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }});
|
Entities.editEntity(entity1, {
|
||||||
Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }});
|
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
|
// play the sound near the camera so the shooter can hear it
|
||||||
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
||||||
|
@ -190,19 +211,32 @@ function shootBullet(position, velocity, grenade) {
|
||||||
var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE;
|
var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE;
|
||||||
var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
|
var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
|
||||||
|
|
||||||
bulletID = Entities.addEntity(
|
bulletID = Entities.addEntity({
|
||||||
{ type: "Sphere",
|
type: "Sphere",
|
||||||
position: position,
|
position: position,
|
||||||
dimensions: { x: bSize, y: bSize, z: bSize },
|
dimensions: {
|
||||||
color: { red: 0, green: 0, blue: 0 },
|
x: bSize,
|
||||||
|
y: bSize,
|
||||||
|
z: bSize
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 0
|
||||||
|
},
|
||||||
velocity: bVelocity,
|
velocity: bVelocity,
|
||||||
lifetime: BULLET_LIFETIME,
|
lifetime: BULLET_LIFETIME,
|
||||||
gravity: { x: 0, y: bGravity, z: 0 },
|
gravity: {
|
||||||
|
x: 0,
|
||||||
|
y: bGravity,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
damping: 0.01,
|
damping: 0.01,
|
||||||
density: 8000,
|
density: 8000,
|
||||||
ignoreCollisions: false,
|
ignoreCollisions: false,
|
||||||
collisionsWillMove: true
|
collisionsWillMove: true
|
||||||
});
|
});
|
||||||
|
|
||||||
Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity);
|
Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity);
|
||||||
|
|
||||||
// Play firing sounds
|
// Play firing sounds
|
||||||
|
@ -212,17 +246,8 @@ function shootBullet(position, velocity, grenade) {
|
||||||
if ((shotsFired % RELOAD_INTERVAL) == 0) {
|
if ((shotsFired % RELOAD_INTERVAL) == 0) {
|
||||||
Audio.playSound(loadSound, audioOptions);
|
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() {
|
function shootTarget() {
|
||||||
var TARGET_SIZE = 0.50;
|
var TARGET_SIZE = 0.50;
|
||||||
var TARGET_GRAVITY = 0.0;
|
var TARGET_GRAVITY = 0.0;
|
||||||
|
@ -233,7 +258,11 @@ function shootTarget() {
|
||||||
var ANGLE_RANGE_FOR_LAUNCH = 20.0;
|
var ANGLE_RANGE_FOR_LAUNCH = 20.0;
|
||||||
var camera = Camera.getPosition();
|
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);
|
targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection);
|
||||||
var forwardVector = Quat.getFront(targetDirection);
|
var forwardVector = Quat.getFront(targetDirection);
|
||||||
|
|
||||||
|
@ -242,18 +271,31 @@ function shootTarget() {
|
||||||
var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY);
|
var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY);
|
||||||
velocity.y += TARGET_UP_VELOCITY;
|
velocity.y += TARGET_UP_VELOCITY;
|
||||||
|
|
||||||
targetID = Entities.addEntity(
|
targetID = Entities.addEntity({
|
||||||
{ type: "Box",
|
type: "Box",
|
||||||
position: newPosition,
|
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 },
|
dimensions: {
|
||||||
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
|
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,
|
velocity: velocity,
|
||||||
gravity: { x: 0, y: TARGET_GRAVITY, z: 0 },
|
gravity: {
|
||||||
|
x: 0,
|
||||||
|
y: TARGET_GRAVITY,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
lifetime: TARGET_LIFETIME,
|
lifetime: TARGET_LIFETIME,
|
||||||
rotation: Camera.getOrientation(),
|
rotation: Camera.getOrientation(),
|
||||||
damping: 0.1,
|
damping: 0.1,
|
||||||
density: 100.0,
|
density: 100.0,
|
||||||
collisionsWillMove: true });
|
collisionsWillMove: true
|
||||||
|
});
|
||||||
|
|
||||||
// Record start time
|
// Record start time
|
||||||
shotTime = new Date();
|
shotTime = new Date();
|
||||||
|
@ -274,20 +316,41 @@ function makeGrid(type, scale, size) {
|
||||||
for (y = 0; y < size; y++) {
|
for (y = 0; y < size; y++) {
|
||||||
for (z = 0; z < size; z++) {
|
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 };
|
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(
|
Entities.addEntity({
|
||||||
{ type: type,
|
type: type,
|
||||||
position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation },
|
position: {
|
||||||
|
x: pos.x + x * separation,
|
||||||
|
y: pos.y + y * separation,
|
||||||
|
z: pos.z + z * separation
|
||||||
|
},
|
||||||
dimensions: dimensions,
|
dimensions: dimensions,
|
||||||
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
|
color: {
|
||||||
velocity: { x: 0, y: 0, z: 0 },
|
red: Math.random() * 255,
|
||||||
gravity: { x: 0, y: 0, z: 0 },
|
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,
|
lifetime: GRID_LIFE,
|
||||||
rotation: Camera.getOrientation(),
|
rotation: Camera.getOrientation(),
|
||||||
damping: 0.1,
|
damping: 0.1,
|
||||||
density: 100.0,
|
density: 100.0,
|
||||||
collisionsWillMove: true });
|
collisionsWillMove: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,21 +369,40 @@ function makePlatform(gravity, scale, size) {
|
||||||
for (y = 0; y < size; y++) {
|
for (y = 0; y < size; y++) {
|
||||||
for (z = 0; z < size; z++) {
|
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(
|
Entities.addEntity({
|
||||||
{ type: "Box",
|
type: "Box",
|
||||||
position: { x: pos.x - (separation * size / 2.0) + x * separation,
|
position: {
|
||||||
|
x: pos.x - (separation * size / 2.0) + x * separation,
|
||||||
y: pos.y + y * (separation + INITIAL_GAP),
|
y: pos.y + y * (separation + INITIAL_GAP),
|
||||||
z: pos.z - (separation * size / 2.0) + z * separation },
|
z: pos.z - (separation * size / 2.0) + z * separation
|
||||||
|
},
|
||||||
dimensions: dimensions,
|
dimensions: dimensions,
|
||||||
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
|
color: {
|
||||||
velocity: { x: 0, y: 0.05, z: 0 },
|
red: Math.random() * 255,
|
||||||
gravity: { x: 0, y: gravity, z: 0 },
|
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,
|
lifetime: TARGET_LIFE,
|
||||||
damping: 0.1,
|
damping: 0.1,
|
||||||
density: 100.0,
|
density: 100.0,
|
||||||
collisionsWillMove: true });
|
collisionsWillMove: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,9 +410,21 @@ function makePlatform(gravity, scale, size) {
|
||||||
// Make a floor for this stuff to fall onto
|
// Make a floor for this stuff to fall onto
|
||||||
Entities.addEntity({
|
Entities.addEntity({
|
||||||
type: "Box",
|
type: "Box",
|
||||||
position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z },
|
position: {
|
||||||
dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size },
|
x: pos.x,
|
||||||
color: { red: 100, green: 100, blue: 100 },
|
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
|
lifetime: TARGET_LIFE
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -347,25 +441,12 @@ function keyPressEvent(event) {
|
||||||
shootFromMouse(true);
|
shootFromMouse(true);
|
||||||
} else if (event.text == "r") {
|
} else if (event.text == "r") {
|
||||||
playLoadSound();
|
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() {
|
function playLoadSound() {
|
||||||
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
audioOptions.position = MyAvatar.leftHandPose.translation;
|
||||||
Audio.playSound(loadSound, audioOptions);
|
Audio.playSound(loadSound, audioOptions);
|
||||||
// Raise arm to firing posture
|
|
||||||
takeFiringPose();
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearPose() {
|
|
||||||
MyAvatar.clearJointData("LeftForeArm");
|
|
||||||
MyAvatar.clearJointData("LeftArm");
|
|
||||||
MyAvatar.clearJointData("LeftHand");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteBulletAndTarget() {
|
function deleteBulletAndTarget() {
|
||||||
|
@ -375,118 +456,57 @@ function deleteBulletAndTarget() {
|
||||||
targetID = 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) {
|
function update(deltaTime) {
|
||||||
if (activeControllers == 0) {
|
// FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2]
|
||||||
if (Controller.getNumberOfSpatialControls() > 0) {
|
var tipPoses = [ MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose ];
|
||||||
activeControllers = Controller.getNumberOfSpatialControls();
|
|
||||||
clearPose();
|
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
|
||||||
|
});
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var KICKBACK_DECAY_RATE = 0.125;
|
// Need to adjust the laser
|
||||||
if (elbowKickAngle > 0.0) {
|
var tipPose = tipPoses[side];
|
||||||
if (elbowKickAngle > 0.5) {
|
var handRotation = tipPoses[side].rotation;
|
||||||
var newAngle = elbowKickAngle * KICKBACK_DECAY_RATE;
|
var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]);
|
||||||
elbowKickAngle -= newAngle;
|
barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset);
|
||||||
var armRotation = MyAvatar.getJointRotation("LeftForeArm");
|
barrelVectors[side] = Vec3.multiplyQbyV(handRotation, {
|
||||||
armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, -newAngle));
|
x: 0,
|
||||||
MyAvatar.setJointData("LeftForeArm", armRotation);
|
y: 1,
|
||||||
} else {
|
z: 0
|
||||||
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
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (shootABullet) {
|
var laserTip = Vec3.sum(Vec3.multiply(100.0, barrelVectors[side]), barrelTips[side]);
|
||||||
|
// Update Lasers
|
||||||
var palmToFingerTipVector =
|
Overlays.editOverlay(pointer[side], {
|
||||||
{ x: (fingerTipPosition.x - palmPosition.x),
|
start: barrelTips[side],
|
||||||
y: (fingerTipPosition.y - palmPosition.y),
|
end: laserTip,
|
||||||
z: (fingerTipPosition.z - palmPosition.z) };
|
alpha: 1,
|
||||||
|
visible: true
|
||||||
// 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));
|
|
||||||
|
|
||||||
|
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);
|
shootBullet(position, velocity, false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 mousePressEvent(event) {
|
function mousePressEvent(event) {
|
||||||
var clickedText = false;
|
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
x: event.x,
|
||||||
|
y: event.y
|
||||||
|
});
|
||||||
if (clickedOverlay == offButton) {
|
if (clickedOverlay == offButton) {
|
||||||
Script.stop();
|
Script.stop();
|
||||||
} else if (clickedOverlay == platformButton) {
|
} else if (clickedOverlay == platformButton) {
|
||||||
|
@ -498,21 +518,33 @@ function mousePressEvent(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function scriptEnding() {
|
function scriptEnding() {
|
||||||
Overlays.deleteOverlay(reticle);
|
mapping.disable();
|
||||||
toolBar.cleanup();
|
toolBar.cleanup();
|
||||||
Overlays.deleteOverlay(pointer[0]);
|
for (var i = 0; i < pointer.length; ++i) {
|
||||||
Overlays.deleteOverlay(pointer[1]);
|
Overlays.deleteOverlay(pointer[i]);
|
||||||
|
}
|
||||||
Overlays.deleteOverlay(text);
|
Overlays.deleteOverlay(text);
|
||||||
MyAvatar.detachOne(gunModel);
|
MyAvatar.detachOne(GUN_MODEL);
|
||||||
MyAvatar.detachOne(gunModel);
|
MyAvatar.detachOne(GUN_MODEL);
|
||||||
clearPose();
|
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.scriptEnding.connect(scriptEnding);
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
|
||||||
Controller.mousePressEvent.connect(mousePressEvent);
|
Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
Controller.keyPressEvent.connect(keyPressEvent);
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,101 +14,183 @@
|
||||||
#include <QtCore/QJsonObject>
|
#include <QtCore/QJsonObject>
|
||||||
#include <QtCore/QJsonArray>
|
#include <QtCore/QJsonArray>
|
||||||
|
|
||||||
#include "SharedUtil.h"
|
#include <SharedUtil.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;
|
using namespace controller;
|
||||||
|
|
||||||
Filter::Factory Filter::_factory;
|
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(ConstrainToIntegerFilter, "constrainToInteger")
|
||||||
REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger")
|
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(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")
|
REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse")
|
||||||
|
|
||||||
|
|
||||||
const QString JSON_FILTER_TYPE = QStringLiteral("type");
|
const QString JSON_FILTER_TYPE = QStringLiteral("type");
|
||||||
const QString JSON_FILTER_PARAMS = QStringLiteral("params");
|
const QString JSON_FILTER_PARAMS = QStringLiteral("params");
|
||||||
|
|
||||||
|
|
||||||
Filter::Pointer Filter::parse(const QJsonObject& json) {
|
Filter::Pointer Filter::parse(const QJsonValue& json) {
|
||||||
// The filter is an object, now let s check for type and potential arguments
|
|
||||||
Filter::Pointer filter;
|
Filter::Pointer filter;
|
||||||
auto filterType = json[JSON_FILTER_TYPE];
|
if (json.isString()) {
|
||||||
if (filterType.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());
|
filter = Filter::getFactory().create(filterType.toString());
|
||||||
if (filter) {
|
if (filter) {
|
||||||
// Filter is defined, need to read the parameters and validate
|
QJsonValue params = jsonObj;
|
||||||
auto parameters = json[JSON_FILTER_PARAMS];
|
if (jsonObj.contains(JSON_FILTER_PARAMS)) {
|
||||||
if (parameters.isArray()) {
|
params = jsonObj[JSON_FILTER_PARAMS];
|
||||||
if (filter->parseParameters(parameters.toArray())) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (!filter->parseParameters(params)) {
|
||||||
|
qWarning() << "Unable to parse filter parameters " << params;
|
||||||
return Filter::Pointer();
|
return Filter::Pointer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output) {
|
||||||
bool ScaleFilter::parseParameters(const QJsonArray& parameters) {
|
if (parameters.isDouble()) {
|
||||||
if (parameters.size() > 1) {
|
output = parameters.toDouble();
|
||||||
_scale = parameters[0].toDouble();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if (parameters.isArray()) {
|
||||||
|
auto arrayParameters = parameters.toArray();
|
||||||
|
if (arrayParameters.size() > 1) {
|
||||||
bool ClampFilter::parseParameters(const QJsonArray& parameters) {
|
output = arrayParameters[0].toDouble();
|
||||||
if (parameters.size() > 1) {
|
|
||||||
_min = parameters[0].toDouble();
|
|
||||||
}
|
|
||||||
if (parameters.size() > 2) {
|
|
||||||
_max = parameters[1].toDouble();
|
|
||||||
}
|
|
||||||
return true;
|
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();
|
|
||||||
}
|
}
|
||||||
|
} 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 true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float PulseFilter::apply(float value) const {
|
|
||||||
float result = 0.0f;
|
|
||||||
|
|
||||||
if (0.0f != value) {
|
#if 0
|
||||||
float now = secTimestampNow();
|
|
||||||
float delta = now - _lastEmitTime;
|
namespace controller {
|
||||||
if (delta >= _interval) {
|
|
||||||
_lastEmitTime = now;
|
class LambdaFilter : public Filter {
|
||||||
result = value;
|
public:
|
||||||
}
|
// LambdaFilter() {}12
|
||||||
|
LambdaFilter(Lambda f) : _function(f) {};
|
||||||
|
|
||||||
|
virtual float apply(float value) const {
|
||||||
|
return _function(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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
|
||||||
bool PulseFilter::parseParameters(const QJsonArray& parameters) {
|
|
||||||
if (parameters.size() > 1) {
|
|
||||||
_interval = parameters[0].toDouble();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,7 @@
|
||||||
|
|
||||||
#include <QtCore/QEasingCurve>
|
#include <QtCore/QEasingCurve>
|
||||||
|
|
||||||
class QJsonObject;
|
class QJsonValue;
|
||||||
class QJsonArray;
|
|
||||||
|
|
||||||
namespace controller {
|
namespace controller {
|
||||||
|
|
||||||
|
@ -36,11 +35,13 @@ namespace controller {
|
||||||
|
|
||||||
virtual float apply(float value) const = 0;
|
virtual float apply(float value) const = 0;
|
||||||
// Factory features
|
// 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 void registerBuilder(const QString& name, Factory::Builder builder);
|
||||||
static Factory& getFactory() { return _factory; }
|
static Factory& getFactory() { return _factory; }
|
||||||
|
|
||||||
|
static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output);
|
||||||
protected:
|
protected:
|
||||||
static Factory _factory;
|
static Factory _factory;
|
||||||
};
|
};
|
||||||
|
@ -54,194 +55,5 @@ namespace controller {
|
||||||
#define REGISTER_FILTER_CLASS_INSTANCE(classEntry, className) \
|
#define REGISTER_FILTER_CLASS_INSTANCE(classEntry, className) \
|
||||||
classEntry::Registrar classEntry::_registrar(className, Filter::getFactory());
|
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<float>::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
|
#endif
|
||||||
|
|
|
@ -18,6 +18,15 @@
|
||||||
#include "../ScriptingInterface.h"
|
#include "../ScriptingInterface.h"
|
||||||
#include "../Logging.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;
|
using namespace controller;
|
||||||
|
|
||||||
void RouteBuilderProxy::toQml(const QJSValue& destination) {
|
void RouteBuilderProxy::toQml(const QJSValue& destination) {
|
||||||
|
@ -43,18 +52,6 @@ QObject* RouteBuilderProxy::debug(bool enable) {
|
||||||
return this;
|
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) {
|
QObject* RouteBuilderProxy::when(const QScriptValue& expression) {
|
||||||
_route->conditional = _parent.conditionalFor(expression);
|
_route->conditional = _parent.conditionalFor(expression);
|
||||||
return this;
|
return this;
|
||||||
|
@ -65,53 +62,46 @@ QObject* RouteBuilderProxy::whenQml(const QJSValue& expression) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QObject* RouteBuilderProxy::filter(const QScriptValue& expression) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QObject* RouteBuilderProxy::clamp(float min, float max) {
|
QObject* RouteBuilderProxy::clamp(float min, float max) {
|
||||||
addFilter(Filter::Pointer(new ClampFilter(min, max)));
|
addFilter(std::make_shared<ClampFilter>(min, max));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* RouteBuilderProxy::scale(float multiplier) {
|
QObject* RouteBuilderProxy::scale(float multiplier) {
|
||||||
addFilter(Filter::Pointer(new ScaleFilter(multiplier)));
|
addFilter(std::make_shared<ScaleFilter>(multiplier));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* RouteBuilderProxy::invert() {
|
QObject* RouteBuilderProxy::invert() {
|
||||||
addFilter(Filter::Pointer(new InvertFilter()));
|
addFilter(std::make_shared<InvertFilter>());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject* RouteBuilderProxy::hysteresis(float min, float max) {
|
||||||
|
addFilter(std::make_shared<HysteresisFilter>(min, max));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* RouteBuilderProxy::deadZone(float min) {
|
QObject* RouteBuilderProxy::deadZone(float min) {
|
||||||
addFilter(Filter::Pointer(new DeadZoneFilter(min)));
|
addFilter(std::make_shared<DeadZoneFilter>(min));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* RouteBuilderProxy::constrainToInteger() {
|
QObject* RouteBuilderProxy::constrainToInteger() {
|
||||||
addFilter(Filter::Pointer(new ConstrainToIntegerFilter()));
|
addFilter(std::make_shared<ConstrainToIntegerFilter>());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* RouteBuilderProxy::constrainToPositiveInteger() {
|
QObject* RouteBuilderProxy::constrainToPositiveInteger() {
|
||||||
addFilter(Filter::Pointer(new ConstrainToPositiveIntegerFilter()));
|
addFilter(std::make_shared<ConstrainToPositiveIntegerFilter>());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QObject* RouteBuilderProxy::pulse(float interval) {
|
QObject* RouteBuilderProxy::pulse(float interval) {
|
||||||
addFilter(Filter::Pointer(new PulseFilter(interval)));
|
addFilter(std::make_shared<PulseFilter>(interval));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RouteBuilderProxy::addFilter(Filter::Lambda lambda) {
|
|
||||||
Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda);
|
|
||||||
addFilter(filterPointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouteBuilderProxy::addFilter(Filter::Pointer filter) {
|
void RouteBuilderProxy::addFilter(Filter::Pointer filter) {
|
||||||
_route->filters.push_back(filter);
|
_route->filters.push_back(filter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,14 +34,13 @@ class RouteBuilderProxy : public QObject {
|
||||||
: _parent(parent), _mapping(mapping), _route(route) { }
|
: _parent(parent), _mapping(mapping), _route(route) { }
|
||||||
|
|
||||||
Q_INVOKABLE void toQml(const QJSValue& destination);
|
Q_INVOKABLE void toQml(const QJSValue& destination);
|
||||||
Q_INVOKABLE QObject* filterQml(const QJSValue& expression);
|
|
||||||
Q_INVOKABLE QObject* whenQml(const QJSValue& expression);
|
Q_INVOKABLE QObject* whenQml(const QJSValue& expression);
|
||||||
|
|
||||||
Q_INVOKABLE void to(const QScriptValue& destination);
|
Q_INVOKABLE void to(const QScriptValue& destination);
|
||||||
Q_INVOKABLE QObject* debug(bool enable = true);
|
Q_INVOKABLE QObject* debug(bool enable = true);
|
||||||
Q_INVOKABLE QObject* when(const QScriptValue& expression);
|
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* clamp(float min, float max);
|
||||||
|
Q_INVOKABLE QObject* hysteresis(float min, float max);
|
||||||
Q_INVOKABLE QObject* pulse(float interval);
|
Q_INVOKABLE QObject* pulse(float interval);
|
||||||
Q_INVOKABLE QObject* scale(float multiplier);
|
Q_INVOKABLE QObject* scale(float multiplier);
|
||||||
Q_INVOKABLE QObject* invert();
|
Q_INVOKABLE QObject* invert();
|
||||||
|
@ -52,7 +51,6 @@ class RouteBuilderProxy : public QObject {
|
||||||
private:
|
private:
|
||||||
void to(const Endpoint::Pointer& destination);
|
void to(const Endpoint::Pointer& destination);
|
||||||
void conditional(const Conditional::Pointer& conditional);
|
void conditional(const Conditional::Pointer& conditional);
|
||||||
void addFilter(Filter::Lambda lambda);
|
|
||||||
void addFilter(Filter::Pointer filter);
|
void addFilter(Filter::Pointer filter);
|
||||||
UserInputMapper& _parent;
|
UserInputMapper& _parent;
|
||||||
Mapping::Pointer _mapping;
|
Mapping::Pointer _mapping;
|
||||||
|
|
|
@ -14,7 +14,7 @@ using namespace controller;
|
||||||
|
|
||||||
float ScriptEndpoint::value() {
|
float ScriptEndpoint::value() {
|
||||||
updateValue();
|
updateValue();
|
||||||
return _lastValue;
|
return _lastValueRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEndpoint::updateValue() {
|
void ScriptEndpoint::updateValue() {
|
||||||
|
@ -23,14 +23,18 @@ void ScriptEndpoint::updateValue() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastValue = (float)_callable.call().toNumber();
|
_lastValueRead = (float)_callable.call().toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) {
|
void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) {
|
||||||
|
if (newValue == _lastValueWritten) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
internalApply(newValue, oldValue, source->getInput().getID());
|
internalApply(newValue, oldValue, source->getInput().getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) {
|
void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) {
|
||||||
|
_lastValueWritten = newValue;
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection,
|
QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection,
|
||||||
Q_ARG(float, newValue),
|
Q_ARG(float, newValue),
|
||||||
|
|
|
@ -31,7 +31,8 @@ protected:
|
||||||
Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID);
|
Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID);
|
||||||
private:
|
private:
|
||||||
QScriptValue _callable;
|
QScriptValue _callable;
|
||||||
float _lastValue = 0.0f;
|
float _lastValueRead { 0.0f };
|
||||||
|
float _lastValueWritten { 0.0f };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <QtCore/QJsonObject>
|
||||||
|
#include <QtCore/QJsonArray>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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 <QtCore/QJsonObject>
|
||||||
|
#include <QtCore/QJsonArray>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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 <QtCore/QJsonObject>
|
||||||
|
#include <QtCore/QJsonArray>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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 <QtCore/QJsonObject>
|
||||||
|
#include <QtCore/QJsonArray>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -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<float>::max() };
|
||||||
|
float _interval = 1.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -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 <QtCore/QJsonObject>
|
||||||
|
#include <QtCore/QJsonArray>
|
||||||
|
|
||||||
|
using namespace controller;
|
||||||
|
|
||||||
|
bool ScaleFilter::parseParameters(const QJsonValue& parameters) {
|
||||||
|
static const QString JSON_SCALE = QStringLiteral("scale");
|
||||||
|
return parseSingleFloatParameter(parameters, JSON_SCALE, _scale);
|
||||||
|
}
|
|
@ -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
|
Loading…
Reference in a new issue