mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
Merge branch 'master' of ssh://github.com/highfidelity/hifi into avatar-interaction
Conflicts: interface/src/avatar/Hand.h interface/src/avatar/MyAvatar.cpp
This commit is contained in:
commit
439cbe3f87
23 changed files with 530 additions and 327 deletions
|
@ -69,6 +69,12 @@ function checkCamera() {
|
|||
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
|
||||
yaw -= viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime;
|
||||
pitch += viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime;
|
||||
if (yaw > 360) {
|
||||
yaw -= 360;
|
||||
}
|
||||
if (yaw < -360) {
|
||||
yaw += 360;
|
||||
}
|
||||
var orientation = Quat.fromPitchYawRoll(pitch, yaw, roll);
|
||||
Camera.setOrientation(orientation);
|
||||
}
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
//
|
||||
// Captures mouse clicks and edits voxels accordingly.
|
||||
//
|
||||
// click = create a new voxel on this face, same color as old
|
||||
// Alt + click = delete this voxel
|
||||
// click = create a new voxel on this face, same color as old (default color picker state)
|
||||
// right click or control + click = delete this voxel
|
||||
// shift + click = recolor this voxel
|
||||
// 1 - 8 = pick new color from palette
|
||||
// 9 = create a new voxel in front of the camera
|
||||
//
|
||||
// Click and drag to create more new voxels in the same direction
|
||||
//
|
||||
|
@ -18,15 +20,41 @@ function vLength(v) {
|
|||
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||
}
|
||||
|
||||
function vMinus(a, b) {
|
||||
var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
|
||||
return rval;
|
||||
}
|
||||
|
||||
var NEW_VOXEL_SIZE = 1.0;
|
||||
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
|
||||
var ORBIT_RATE_ALTITUDE = 200.0;
|
||||
var ORBIT_RATE_AZIMUTH = 90.0;
|
||||
var PIXELS_PER_EXTRUDE_VOXEL = 16;
|
||||
|
||||
var oldMode = Camera.getMode();
|
||||
|
||||
var key_alt = false;
|
||||
var key_shift = false;
|
||||
var isAdding = false;
|
||||
|
||||
var isExtruding = false;
|
||||
var isOrbiting = false;
|
||||
var orbitAzimuth = 0.0;
|
||||
var orbitAltitude = 0.0;
|
||||
var orbitCenter = { x: 0, y: 0, z: 0 };
|
||||
var orbitPosition = { x: 0, y: 0, z: 0 };
|
||||
var orbitRadius = 0.0;
|
||||
var extrudeDirection = { x: 0, y: 0, z: 0 };
|
||||
var extrudeScale = 0.0;
|
||||
var lastVoxelPosition = { x: 0, y: 0, z: 0 };
|
||||
var lastVoxelColor = { red: 0, green: 0, blue: 0 };
|
||||
var lastVoxelScale = 0;
|
||||
var dragStart = { x: 0, y: 0 };
|
||||
|
||||
var mouseX = 0;
|
||||
var mouseY = 0;
|
||||
|
||||
|
||||
|
||||
// Create a table of the different colors you can choose
|
||||
var colors = new Array();
|
||||
colors[0] = { red: 237, green: 175, blue: 0 };
|
||||
|
@ -37,31 +65,70 @@ colors[4] = { red: 193, green: 99, blue: 122 };
|
|||
colors[5] = { red: 255, green: 54, blue: 69 };
|
||||
colors[6] = { red: 124, green: 36, blue: 36 };
|
||||
colors[7] = { red: 63, green: 35, blue: 19 };
|
||||
var numColors = 6;
|
||||
var whichColor = 0;
|
||||
var numColors = 8;
|
||||
var whichColor = -1; // Starting color is 'Copy' mode
|
||||
|
||||
// Create sounds for adding, deleting, recoloring voxels
|
||||
var addSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Electronic/ElectronicBurst1.raw");
|
||||
var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Bubbles/bubbles1.raw");
|
||||
var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Electronic/ElectronicBurst6.raw");
|
||||
var addSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create.raw");
|
||||
var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+delete.raw");
|
||||
var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+edit.raw");
|
||||
var clickSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Switches+and+sliders/toggle+switch+-+medium.raw");
|
||||
var audioOptions = new AudioInjectionOptions();
|
||||
audioOptions.volume = 0.5;
|
||||
|
||||
function setAudioPosition() {
|
||||
var camera = Camera.getPosition();
|
||||
var forwardVector = Quat.getFront(MyAvatar.orientation);
|
||||
audioOptions.position = Vec3.sum(camera, forwardVector);
|
||||
}
|
||||
|
||||
function getNewVoxelPosition() {
|
||||
var camera = Camera.getPosition();
|
||||
var forwardVector = Quat.getFront(MyAvatar.orientation);
|
||||
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, NEW_VOXEL_DISTANCE_FROM_CAMERA));
|
||||
return newPosition;
|
||||
}
|
||||
|
||||
function fixEulerAngles(eulers) {
|
||||
var rVal = { x: 0, y: 0, z: eulers.z };
|
||||
if (eulers.x >= 90.0) {
|
||||
rVal.x = 180.0 - eulers.x;
|
||||
rVal.y = eulers.y - 180.0;
|
||||
} else if (eulers.x <= -90.0) {
|
||||
rVal.x = 180.0 - eulers.x;
|
||||
rVal.y = eulers.y - 180.0;
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
mouseX = event.x;
|
||||
mouseY = event.y;
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Voxels.findRayIntersection(pickRay);
|
||||
audioOptions.volume = 1.0;
|
||||
audioOptions.position = { x: intersection.voxel.x, y: intersection.voxel.y, z: intersection.voxel.z };
|
||||
|
||||
audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction);
|
||||
if (intersection.intersects) {
|
||||
if (key_alt) {
|
||||
if (event.isAlt) {
|
||||
// start orbit camera!
|
||||
var cameraPosition = Camera.getPosition();
|
||||
oldMode = Camera.getMode();
|
||||
Camera.setMode("independent");
|
||||
isOrbiting = true;
|
||||
Camera.keepLookingAt(intersection.intersection);
|
||||
// get position for initial azimuth, elevation
|
||||
orbitCenter = intersection.intersection;
|
||||
var orbitVector = Vec3.subtract(cameraPosition, orbitCenter);
|
||||
orbitRadius = vLength(orbitVector);
|
||||
orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x);
|
||||
orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector));
|
||||
|
||||
} else if (event.isRightButton || event.isControl) {
|
||||
// Delete voxel
|
||||
Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
|
||||
Audio.playSound(deleteSound, audioOptions);
|
||||
|
||||
} else if (key_shift) {
|
||||
} else if (event.isShifted) {
|
||||
// Recolor Voxel
|
||||
whichColor++;
|
||||
if (whichColor == numColors) whichColor = 0;
|
||||
Voxels.setVoxel(intersection.voxel.x,
|
||||
intersection.voxel.y,
|
||||
intersection.voxel.z,
|
||||
|
@ -70,7 +137,9 @@ function mousePressEvent(event) {
|
|||
Audio.playSound(changeColorSound, audioOptions);
|
||||
} else {
|
||||
// Add voxel on face
|
||||
var newVoxel = {
|
||||
if (whichColor == -1) {
|
||||
// Copy mode - use clicked voxel color
|
||||
var newVoxel = {
|
||||
x: intersection.voxel.x,
|
||||
y: intersection.voxel.y,
|
||||
z: intersection.voxel.z,
|
||||
|
@ -78,6 +147,16 @@ function mousePressEvent(event) {
|
|||
red: intersection.voxel.red,
|
||||
green: intersection.voxel.green,
|
||||
blue: intersection.voxel.blue };
|
||||
} else {
|
||||
var newVoxel = {
|
||||
x: intersection.voxel.x,
|
||||
y: intersection.voxel.y,
|
||||
z: intersection.voxel.z,
|
||||
s: intersection.voxel.s,
|
||||
red: colors[whichColor].red,
|
||||
green: colors[whichColor].green,
|
||||
blue: colors[whichColor].blue };
|
||||
}
|
||||
|
||||
if (intersection.face == "MIN_X_FACE") {
|
||||
newVoxel.x -= newVoxel.s;
|
||||
|
@ -108,55 +187,113 @@ function mousePressEvent(event) {
|
|||
function keyPressEvent(event) {
|
||||
key_alt = event.isAlt;
|
||||
key_shift = event.isShifted;
|
||||
var nVal = parseInt(event.text);
|
||||
if (event.text == "0") {
|
||||
print("Color = Copy");
|
||||
whichColor = -1;
|
||||
Audio.playSound(clickSound, audioOptions);
|
||||
} else if ((nVal > 0) && (nVal <= numColors)) {
|
||||
whichColor = nVal - 1;
|
||||
print("Color = " + (whichColor + 1));
|
||||
Audio.playSound(clickSound, audioOptions);
|
||||
} else if (event.text == "9") {
|
||||
// Create a brand new 1 meter voxel in front of your avatar
|
||||
var color = whichColor;
|
||||
if (color == -1) color = 0;
|
||||
var newPosition = getNewVoxelPosition();
|
||||
var newVoxel = {
|
||||
x: newPosition.x,
|
||||
y: newPosition.y ,
|
||||
z: newPosition.z,
|
||||
s: NEW_VOXEL_SIZE,
|
||||
red: colors[color].red,
|
||||
green: colors[color].green,
|
||||
blue: colors[color].blue };
|
||||
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
||||
setAudioPosition();
|
||||
Audio.playSound(addSound, audioOptions);
|
||||
} else if (event.text == " ") {
|
||||
// Reset my orientation!
|
||||
var orientation = { x:0, y:0, z:0, w:1 };
|
||||
Camera.setOrientation(orientation);
|
||||
MyAvatar.orientation = orientation;
|
||||
}
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
key_alt = false;
|
||||
key_shift = false;
|
||||
}
|
||||
function mouseMoveEvent(event) {
|
||||
if (isOrbiting) {
|
||||
var cameraOrientation = Camera.getOrientation();
|
||||
var origEulers = Quat.safeEulerAngles(cameraOrientation);
|
||||
var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation));
|
||||
var dx = event.x - mouseX;
|
||||
var dy = event.y - mouseY;
|
||||
orbitAzimuth += dx / ORBIT_RATE_AZIMUTH;
|
||||
orbitAltitude += dy / ORBIT_RATE_ALTITUDE;
|
||||
var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius,
|
||||
y:Math.sin(orbitAltitude) * orbitRadius,
|
||||
z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius };
|
||||
orbitPosition = Vec3.sum(orbitCenter, orbitVector);
|
||||
Camera.setPosition(orbitPosition);
|
||||
mouseX = event.x;
|
||||
mouseY = event.y;
|
||||
}
|
||||
if (isAdding) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x,
|
||||
// Watch the drag direction to tell which way to 'extrude' this voxel
|
||||
if (!isExtruding) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x,
|
||||
y: pickRay.origin.y - lastVoxelPosition.y,
|
||||
z: pickRay.origin.z - lastVoxelPosition.z };
|
||||
var distance = vLength(lastVoxelDistance);
|
||||
var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance };
|
||||
mouseSpot.x += pickRay.origin.x;
|
||||
mouseSpot.y += pickRay.origin.y;
|
||||
mouseSpot.z += pickRay.origin.z;
|
||||
var dx = mouseSpot.x - lastVoxelPosition.x;
|
||||
var dy = mouseSpot.y - lastVoxelPosition.y;
|
||||
var dz = mouseSpot.z - lastVoxelPosition.z;
|
||||
if (dx > lastVoxelScale) {
|
||||
lastVoxelPosition.x += lastVoxelScale;
|
||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
||||
} else if (dx < -lastVoxelScale) {
|
||||
lastVoxelPosition.x -= lastVoxelScale;
|
||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
||||
} else if (dy > lastVoxelScale) {
|
||||
lastVoxelPosition.y += lastVoxelScale;
|
||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
||||
} else if (dy < -lastVoxelScale) {
|
||||
lastVoxelPosition.y -= lastVoxelScale;
|
||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
||||
} else if (dz > lastVoxelScale) {
|
||||
lastVoxelPosition.z += lastVoxelScale;
|
||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
||||
} else if (dz < -lastVoxelScale) {
|
||||
lastVoxelPosition.z -= lastVoxelScale;
|
||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
||||
lastVoxelScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
||||
var distance = vLength(lastVoxelDistance);
|
||||
var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance };
|
||||
mouseSpot.x += pickRay.origin.x;
|
||||
mouseSpot.y += pickRay.origin.y;
|
||||
mouseSpot.z += pickRay.origin.z;
|
||||
var dx = mouseSpot.x - lastVoxelPosition.x;
|
||||
var dy = mouseSpot.y - lastVoxelPosition.y;
|
||||
var dz = mouseSpot.z - lastVoxelPosition.z;
|
||||
extrudeScale = lastVoxelScale;
|
||||
extrudeDirection = { x: 0, y: 0, z: 0 };
|
||||
isExtruding = true;
|
||||
if (dx > lastVoxelScale) extrudeDirection.x = extrudeScale;
|
||||
else if (dx < -lastVoxelScale) extrudeDirection.x = -extrudeScale;
|
||||
else if (dy > lastVoxelScale) extrudeDirection.y = extrudeScale;
|
||||
else if (dy < -lastVoxelScale) extrudeDirection.y = -extrudeScale;
|
||||
else if (dz > lastVoxelScale) extrudeDirection.z = extrudeScale;
|
||||
else if (dz < -lastVoxelScale) extrudeDirection.z = -extrudeScale;
|
||||
else isExtruding = false;
|
||||
} else {
|
||||
// We have got an extrusion direction, now look for mouse move beyond threshold to add new voxel
|
||||
var dx = event.x - mouseX;
|
||||
var dy = event.y - mouseY;
|
||||
if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) {
|
||||
lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection);
|
||||
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
|
||||
extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
||||
mouseX = event.x;
|
||||
mouseY = event.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (isOrbiting) {
|
||||
var cameraOrientation = Camera.getOrientation();
|
||||
var eulers = Quat.safeEulerAngles(cameraOrientation);
|
||||
MyAvatar.position = Camera.getPosition();
|
||||
MyAvatar.orientation = cameraOrientation;
|
||||
Camera.stopLooking();
|
||||
Camera.setMode(oldMode);
|
||||
Camera.setOrientation(cameraOrientation);
|
||||
}
|
||||
isAdding = false;
|
||||
isOrbiting = false;
|
||||
isExtruding = false;
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
|
|
210
examples/hydraMove.js
Normal file
210
examples/hydraMove.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
//
|
||||
// hydraMove.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/10/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Controller and MyAvatar classes to implement
|
||||
// avatar flying through the hydra/controller joysticks
|
||||
//
|
||||
//
|
||||
|
||||
var damping = 0.9;
|
||||
var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z };
|
||||
var joysticksCaptured = false;
|
||||
var THRUST_CONTROLLER = 0;
|
||||
var VIEW_CONTROLLER = 1;
|
||||
var INITIAL_THRUST_MULTPLIER = 1.0;
|
||||
var THRUST_INCREASE_RATE = 1.05;
|
||||
var MAX_THRUST_MULTIPLIER = 75.0;
|
||||
var thrustMultiplier = INITIAL_THRUST_MULTPLIER;
|
||||
var grabDelta = { x: 0, y: 0, z: 0};
|
||||
var grabDeltaVelocity = { x: 0, y: 0, z: 0};
|
||||
var grabStartRotation = { x: 0, y: 0, z: 0, w: 1};
|
||||
var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1};
|
||||
var grabbingWithRightHand = false;
|
||||
var wasGrabbingWithRightHand = false;
|
||||
var grabbingWithLeftHand = false;
|
||||
var wasGrabbingWithLeftHand = false;
|
||||
var EPSILON = 0.000001;
|
||||
var velocity = { x: 0, y: 0, z: 0};
|
||||
var deltaTime = 1/60; // approximately our FPS - maybe better to be elapsed time since last call
|
||||
var THRUST_MAG_UP = 800.0;
|
||||
var THRUST_MAG_DOWN = 300.0;
|
||||
var THRUST_MAG_FWD = 500.0;
|
||||
var THRUST_MAG_BACK = 300.0;
|
||||
var THRUST_MAG_LATERAL = 250.0;
|
||||
var THRUST_JUMP = 120.0;
|
||||
|
||||
var YAW_MAG = 500.0;
|
||||
var PITCH_MAG = 100.0;
|
||||
var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
|
||||
var JOYSTICK_YAW_MAG = YAW_MAG;
|
||||
var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
|
||||
|
||||
|
||||
var LEFT_PALM = 0;
|
||||
var LEFT_BUTTON_4 = 4;
|
||||
var RIGHT_PALM = 2;
|
||||
var RIGHT_BUTTON_4 = 10;
|
||||
|
||||
// Used by handleGrabBehavior() for managing the grab position changes
|
||||
function getAndResetGrabDelta() {
|
||||
var HAND_GRAB_SCALE_DISTANCE = 2.0;
|
||||
var delta = Vec3.multiply(grabDelta, (MyAvatar.scale * HAND_GRAB_SCALE_DISTANCE));
|
||||
grabDelta = { x: 0, y: 0, z: 0};
|
||||
var avatarRotation = MyAvatar.orientation;
|
||||
var result = Vec3.multiplyQbyV(avatarRotation, Vec3.multiply(delta, -1));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Used by handleGrabBehavior() for managing the grab velocity feature
|
||||
function getAndResetGrabDeltaVelocity() {
|
||||
var HAND_GRAB_SCALE_VELOCITY = 50.0;
|
||||
var delta = Vec3.multiply(grabDeltaVelocity, (MyAvatar.scale * HAND_GRAB_SCALE_VELOCITY));
|
||||
grabDeltaVelocity = { x: 0, y: 0, z: 0};
|
||||
var avatarRotation = MyAvatar.orientation;
|
||||
var result = Quat.multiply(avatarRotation, Vec3.multiply(delta, -1));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Used by handleGrabBehavior() for managing the grab rotation feature
|
||||
function getAndResetGrabRotation() {
|
||||
var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation));
|
||||
grabStartRotation = grabCurrentRotation;
|
||||
return quatDiff;
|
||||
}
|
||||
|
||||
// handles all the grab related behavior: position (crawl), velocity (flick), and rotate (twist)
|
||||
function handleGrabBehavior() {
|
||||
// check for and handle grab behaviors
|
||||
grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4);
|
||||
grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4);
|
||||
stoppedGrabbingWithLeftHand = false;
|
||||
stoppedGrabbingWithRightHand = false;
|
||||
|
||||
if (grabbingWithRightHand && !wasGrabbingWithRightHand) {
|
||||
// Just starting grab, capture starting rotation
|
||||
grabStartRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
|
||||
}
|
||||
if (grabbingWithRightHand) {
|
||||
grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(RIGHT_PALM), deltaTime));
|
||||
grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
|
||||
}
|
||||
if (!grabbingWithRightHand && wasGrabbingWithRightHand) {
|
||||
// Just ending grab, capture velocity
|
||||
grabDeltaVelocity = Controller.getSpatialControlVelocity(RIGHT_PALM);
|
||||
stoppedGrabbingWithRightHand = true;
|
||||
}
|
||||
|
||||
if (grabbingWithLeftHand && !wasGrabbingWithLeftHand) {
|
||||
// Just starting grab, capture starting rotation
|
||||
grabStartRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
|
||||
}
|
||||
|
||||
if (grabbingWithLeftHand) {
|
||||
grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(LEFT_PALM), deltaTime));
|
||||
grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
|
||||
}
|
||||
if (!grabbingWithLeftHand && wasGrabbingWithLeftHand) {
|
||||
// Just ending grab, capture velocity
|
||||
grabDeltaVelocity = Controller.getSpatialControlVelocity(LEFT_PALM);
|
||||
stoppedGrabbingWithLeftHand = true;
|
||||
}
|
||||
|
||||
grabbing = grabbingWithRightHand || grabbingWithLeftHand;
|
||||
stoppedGrabbing = stoppedGrabbingWithRightHand || stoppedGrabbingWithLeftHand;
|
||||
|
||||
if (grabbing) {
|
||||
|
||||
// move position
|
||||
var moveFromGrab = getAndResetGrabDelta();
|
||||
if (Vec3.length(moveFromGrab) > EPSILON) {
|
||||
MyAvatar.position = Vec3.sum(MyAvatar.position, moveFromGrab);
|
||||
velocity = { x: 0, y: 0, z: 0};
|
||||
}
|
||||
|
||||
// add some rotation...
|
||||
var deltaRotation = getAndResetGrabRotation();
|
||||
var GRAB_CONTROLLER_TURN_SCALING = 0.5;
|
||||
var euler = Vec3.multiply(Quat.safeEulerAngles(deltaRotation), GRAB_CONTROLLER_TURN_SCALING);
|
||||
|
||||
// Adjust body yaw by yaw from controller
|
||||
var orientation = Quat.multiply(Quat.angleAxis(-euler.y, {x:0, y: 1, z:0}), MyAvatar.orientation);
|
||||
MyAvatar.orientation = orientation;
|
||||
|
||||
// Adjust head pitch from controller
|
||||
MyAvatar.headPitch = MyAvatar.headPitch - euler.x;
|
||||
}
|
||||
|
||||
// add some velocity...
|
||||
if (stoppedGrabbing) {
|
||||
velocity = Vec3.sum(velocity, getAndResetGrabDeltaVelocity());
|
||||
}
|
||||
|
||||
// handle residual velocity
|
||||
if(Vec3.length(velocity) > EPSILON) {
|
||||
MyAvatar.position = Vec3.sum(MyAvatar.position, Vec3.multiply(velocity, deltaTime));
|
||||
// damp velocity
|
||||
velocity = Vec3.multiply(velocity, damping);
|
||||
}
|
||||
|
||||
|
||||
wasGrabbingWithRightHand = grabbingWithRightHand;
|
||||
wasGrabbingWithLeftHand = grabbingWithLeftHand;
|
||||
}
|
||||
|
||||
// Main update function that handles flying and grabbing behaviort
|
||||
function flyWithHydra() {
|
||||
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
|
||||
|
||||
if (thrustJoystickPosition.x != 0 || thrustJoystickPosition.y != 0) {
|
||||
if (thrustMultiplier < MAX_THRUST_MULTIPLIER) {
|
||||
thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE);
|
||||
}
|
||||
var currentOrientation = MyAvatar.orientation;
|
||||
|
||||
var front = Quat.getFront(currentOrientation);
|
||||
var right = Quat.getRight(currentOrientation);
|
||||
var up = Quat.getUp(currentOrientation);
|
||||
|
||||
var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS *
|
||||
thrustJoystickPosition.y * thrustMultiplier * deltaTime);
|
||||
MyAvatar.addThrust(thrustFront);
|
||||
var thrustRight = Vec3.multiply(right, MyAvatar.scale * THRUST_MAG_HAND_JETS *
|
||||
thrustJoystickPosition.x * thrustMultiplier * deltaTime);
|
||||
MyAvatar.addThrust(thrustRight);
|
||||
} else {
|
||||
thrustMultiplier = INITIAL_THRUST_MULTPLIER;
|
||||
}
|
||||
|
||||
// View Controller
|
||||
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
|
||||
if (viewJoystickPosition.x != 0 || viewJoystickPosition.y != 0) {
|
||||
|
||||
// change the body yaw based on our x controller
|
||||
var orientation = MyAvatar.orientation;
|
||||
var deltaOrientation = Quat.fromPitchYawRoll(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0);
|
||||
MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation);
|
||||
|
||||
// change the headPitch based on our x controller
|
||||
//pitch += viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime;
|
||||
var newPitch = MyAvatar.headPitch + (viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime);
|
||||
MyAvatar.headPitch = newPitch;
|
||||
}
|
||||
handleGrabBehavior();
|
||||
}
|
||||
|
||||
Script.willSendVisualDataCallback.connect(flyWithHydra);
|
||||
Controller.captureJoystick(THRUST_CONTROLLER);
|
||||
Controller.captureJoystick(VIEW_CONTROLLER);
|
||||
|
||||
// Map keyPress and mouse move events to our callbacks
|
||||
function scriptEnding() {
|
||||
// re-enabled the standard application for touch events
|
||||
Controller.releaseJoystick(THRUST_CONTROLLER);
|
||||
Controller.releaseJoystick(VIEW_CONTROLLER);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
@ -2146,15 +2146,6 @@ void Application::updateThreads(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::updateParticles(float deltaTime) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateParticles()");
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) {
|
||||
_cloud.simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateMetavoxels(float deltaTime) {
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()");
|
||||
|
@ -2276,7 +2267,6 @@ void Application::update(float deltaTime) {
|
|||
updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
|
||||
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
|
||||
_avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them...
|
||||
updateParticles(deltaTime); // Simulate particle cloud movements
|
||||
updateMetavoxels(deltaTime); // update metavoxels
|
||||
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
|
||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||
|
@ -2711,10 +2701,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
// disable specular lighting for ground and voxels
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR);
|
||||
|
||||
// Draw Cloud Particles
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) {
|
||||
_cloud.render();
|
||||
}
|
||||
// Draw voxels
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
|
@ -4042,7 +4028,7 @@ void Application::loadScript(const QString& fileNameString) {
|
|||
scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree());
|
||||
|
||||
// hook our avatar object into this script engine
|
||||
scriptEngine->setAvatarData( static_cast<Avatar*>(_myAvatar), "MyAvatar");
|
||||
scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features
|
||||
|
||||
CameraScriptableObject* cameraScriptable = new CameraScriptableObject(&_myCamera, &_viewFrustum);
|
||||
scriptEngine->registerGlobalObject("Camera", cameraScriptable);
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
#include "BandwidthMeter.h"
|
||||
#include "Camera.h"
|
||||
#include "Cloud.h"
|
||||
#include "DatagramProcessor.h"
|
||||
#include "Environment.h"
|
||||
#include "GLCanvas.h"
|
||||
|
@ -284,7 +283,6 @@ private:
|
|||
void updateSixense(float deltaTime);
|
||||
void updateSerialDevices(float deltaTime);
|
||||
void updateThreads(float deltaTime);
|
||||
void updateParticles(float deltaTime);
|
||||
void updateMetavoxels(float deltaTime);
|
||||
void updateCamera(float deltaTime);
|
||||
void updateDialogs(float deltaTime);
|
||||
|
@ -351,8 +349,6 @@ private:
|
|||
|
||||
Stars _stars;
|
||||
|
||||
Cloud _cloud;
|
||||
|
||||
VoxelSystem _voxels;
|
||||
VoxelTree _clipboard; // if I copy/paste
|
||||
VoxelImporter* _voxelImporter;
|
||||
|
|
|
@ -37,6 +37,8 @@ static const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_LENGTH_MSECS * NUM_AUDI
|
|||
|
||||
static const float AUDIO_CALLBACK_MSECS = (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float)SAMPLE_RATE * 1000.0;
|
||||
|
||||
static const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300;
|
||||
|
||||
// Mute icon configration
|
||||
static const int ICON_SIZE = 24;
|
||||
static const int ICON_LEFT = 0;
|
||||
|
@ -65,7 +67,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p
|
|||
_measuredJitter(0),
|
||||
_jitterBufferSamples(initialJitterBufferSamples),
|
||||
_lastInputLoudness(0),
|
||||
_averageInputLoudness(0),
|
||||
_noiseGateMeasuredFloor(0),
|
||||
_noiseGateSampleCounter(0),
|
||||
_noiseGateOpen(false),
|
||||
_noiseGateEnabled(true),
|
||||
_noiseGateFramesToClose(0),
|
||||
|
@ -82,6 +85,8 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p
|
|||
{
|
||||
// clear the array of locally injected samples
|
||||
memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
|
||||
// Create the noise sample array
|
||||
_noiseSampleFrames = new float[NUMBER_OF_NOISE_SAMPLE_FRAMES];
|
||||
}
|
||||
|
||||
void Audio::init(QGLWidget *parent) {
|
||||
|
@ -351,26 +356,66 @@ void Audio::handleAudioInput() {
|
|||
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
|
||||
_inputFormat, _desiredInputFormat);
|
||||
|
||||
//
|
||||
// Impose Noise Gate
|
||||
//
|
||||
// The Noise Gate is used to reject constant background noise by measuring the noise
|
||||
// floor observed at the microphone and then opening the 'gate' to allow microphone
|
||||
// signals to be transmitted when the microphone samples average level exceeds a multiple
|
||||
// of the noise floor.
|
||||
//
|
||||
// NOISE_GATE_HEIGHT: How loud you have to speak relative to noise background to open the gate.
|
||||
// Make this value lower for more sensitivity and less rejection of noise.
|
||||
// NOISE_GATE_WIDTH: The number of samples in an audio frame for which the height must be exceeded
|
||||
// to open the gate.
|
||||
// NOISE_GATE_CLOSE_FRAME_DELAY: Once the noise is below the gate height for the frame, how many frames
|
||||
// will we wait before closing the gate.
|
||||
// NOISE_GATE_FRAMES_TO_AVERAGE: How many audio frames should we average together to compute noise floor.
|
||||
// More means better rejection but also can reject continuous things like singing.
|
||||
// NUMBER_OF_NOISE_SAMPLE_FRAMES: How often should we re-evaluate the noise floor?
|
||||
|
||||
|
||||
float loudness = 0;
|
||||
float thisSample = 0;
|
||||
int samplesOverNoiseGate = 0;
|
||||
|
||||
const float NOISE_GATE_HEIGHT = 3.f;
|
||||
const float NOISE_GATE_HEIGHT = 7.f;
|
||||
const int NOISE_GATE_WIDTH = 5;
|
||||
const int NOISE_GATE_CLOSE_FRAME_DELAY = 30;
|
||||
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
|
||||
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
|
||||
|
||||
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
|
||||
thisSample = fabsf(monoAudioSamples[i]);
|
||||
loudness += thisSample;
|
||||
// Noise Reduction: Count peaks above the average loudness
|
||||
if (thisSample > (_averageInputLoudness * NOISE_GATE_HEIGHT)) {
|
||||
if (thisSample > (_noiseGateMeasuredFloor * NOISE_GATE_HEIGHT)) {
|
||||
samplesOverNoiseGate++;
|
||||
}
|
||||
}
|
||||
_lastInputLoudness = loudness / NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||
const float LOUDNESS_AVERAGING_FRAMES = 1000.f; // This will be about 10 seconds
|
||||
_averageInputLoudness = (1.f - 1.f / LOUDNESS_AVERAGING_FRAMES) * _averageInputLoudness + (1.f / LOUDNESS_AVERAGING_FRAMES) * _lastInputLoudness;
|
||||
|
||||
float averageOfAllSampleFrames = 0.f;
|
||||
_noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness;
|
||||
if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
|
||||
float smallestSample = FLT_MAX;
|
||||
for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i+= NOISE_GATE_FRAMES_TO_AVERAGE) {
|
||||
float thisAverage = 0.0f;
|
||||
for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) {
|
||||
thisAverage += _noiseSampleFrames[j];
|
||||
averageOfAllSampleFrames += _noiseSampleFrames[j];
|
||||
}
|
||||
thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE;
|
||||
|
||||
if (thisAverage < smallestSample) {
|
||||
smallestSample = thisAverage;
|
||||
}
|
||||
}
|
||||
averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES;
|
||||
_noiseGateMeasuredFloor = smallestSample;
|
||||
_noiseGateSampleCounter = 0;
|
||||
//qDebug("smallest sample = %.1f, avg of all = %.1f", _noiseGateMeasuredFloor, averageOfAllSampleFrames);
|
||||
}
|
||||
|
||||
if (_noiseGateEnabled) {
|
||||
if (samplesOverNoiseGate > NOISE_GATE_WIDTH) {
|
||||
_noiseGateOpen = true;
|
||||
|
@ -487,7 +532,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
|||
if (!_ringBuffer.isStarved() && _audioOutput->bytesFree() == _audioOutput->bufferSize()) {
|
||||
// we don't have any audio data left in the output buffer
|
||||
// we just starved
|
||||
qDebug() << "Audio output just starved.";
|
||||
//qDebug() << "Audio output just starved.";
|
||||
_ringBuffer.setIsStarved(true);
|
||||
_numFramesDisplayStarve = 10;
|
||||
}
|
||||
|
@ -505,7 +550,7 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
|||
if (!_ringBuffer.isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO
|
||||
+ (_jitterBufferSamples * 2))) {
|
||||
// starved and we don't have enough to start, keep waiting
|
||||
qDebug() << "Buffer is starved and doesn't have enough samples to start. Held back.";
|
||||
//qDebug() << "Buffer is starved and doesn't have enough samples to start. Held back.";
|
||||
} else {
|
||||
// We are either already playing back, or we have enough audio to start playing back.
|
||||
_ringBuffer.setIsStarved(false);
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
|
||||
void render(int screenWidth, int screenHeight);
|
||||
|
||||
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _averageInputLoudness, 0.f); }
|
||||
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); }
|
||||
|
||||
void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; }
|
||||
|
||||
|
@ -109,7 +109,9 @@ private:
|
|||
float _measuredJitter;
|
||||
int16_t _jitterBufferSamples;
|
||||
float _lastInputLoudness;
|
||||
float _averageInputLoudness;
|
||||
float _noiseGateMeasuredFloor;
|
||||
float* _noiseSampleFrames;
|
||||
int _noiseGateSampleCounter;
|
||||
bool _noiseGateOpen;
|
||||
bool _noiseGateEnabled;
|
||||
int _noiseGateFramesToClose;
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
//
|
||||
// Cloud.cpp
|
||||
// interface
|
||||
//
|
||||
// Created by Philip Rosedale on 11/17/12.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <InterfaceConfig.h>
|
||||
#include "Cloud.h"
|
||||
#include "Util.h"
|
||||
#include "Field.h"
|
||||
|
||||
const int NUM_PARTICLES = 100000;
|
||||
const float FIELD_COUPLE = 0.001f;
|
||||
const bool RENDER_FIELD = false;
|
||||
|
||||
Cloud::Cloud() {
|
||||
glm::vec3 box = glm::vec3(PARTICLE_WORLD_SIZE);
|
||||
_bounds = box;
|
||||
_count = NUM_PARTICLES;
|
||||
_particles = new Particle[_count];
|
||||
_field = new Field(PARTICLE_WORLD_SIZE, FIELD_COUPLE);
|
||||
|
||||
for (unsigned int i = 0; i < _count; i++) {
|
||||
_particles[i].position = randVector() * box;
|
||||
const float INIT_VEL_SCALE = 0.03f;
|
||||
_particles[i].velocity = randVector() * ((float)PARTICLE_WORLD_SIZE * INIT_VEL_SCALE);
|
||||
_particles[i].color = randVector();
|
||||
}
|
||||
}
|
||||
|
||||
void Cloud::render() {
|
||||
if (RENDER_FIELD) {
|
||||
_field->render();
|
||||
}
|
||||
|
||||
glPointSize(3.0f);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_POINT_SMOOTH);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glVertexPointer(3, GL_FLOAT, 3 * sizeof(glm::vec3), &_particles[0].position);
|
||||
glColorPointer(3, GL_FLOAT, 3 * sizeof(glm::vec3), &_particles[0].color);
|
||||
glDrawArrays(GL_POINTS, 0, NUM_PARTICLES);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
|
||||
}
|
||||
|
||||
void Cloud::simulate (float deltaTime) {
|
||||
unsigned int i;
|
||||
_field->simulate(deltaTime);
|
||||
for (i = 0; i < _count; ++i) {
|
||||
|
||||
// Update position
|
||||
_particles[i].position += _particles[i].velocity * deltaTime;
|
||||
|
||||
// Decay Velocity (Drag)
|
||||
const float CONSTANT_DAMPING = 0.15f;
|
||||
_particles[i].velocity *= (1.f - CONSTANT_DAMPING * deltaTime);
|
||||
|
||||
// Interact with Field
|
||||
_field->interact(deltaTime, _particles[i].position, _particles[i].velocity);
|
||||
|
||||
// Update color to velocity
|
||||
_particles[i].color = (glm::normalize(_particles[i].velocity) * 0.5f) + 0.5f;
|
||||
|
||||
// Bounce at bounds
|
||||
if ((_particles[i].position.x > _bounds.x) || (_particles[i].position.x < 0.f)) {
|
||||
_particles[i].position.x = glm::clamp(_particles[i].position.x, 0.f, _bounds.x);
|
||||
_particles[i].velocity.x *= -1.f;
|
||||
}
|
||||
if ((_particles[i].position.y > _bounds.y) || (_particles[i].position.y < 0.f)) {
|
||||
_particles[i].position.y = glm::clamp(_particles[i].position.y, 0.f, _bounds.y);
|
||||
_particles[i].velocity.y *= -1.f;
|
||||
}
|
||||
if ((_particles[i].position.z > _bounds.z) || (_particles[i].position.z < 0.f)) {
|
||||
_particles[i].position.z = glm::clamp(_particles[i].position.z, 0.f, _bounds.z);
|
||||
_particles[i].velocity.z *= -1.f;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
//
|
||||
// Cloud.h
|
||||
// interface
|
||||
//
|
||||
// Created by Philip Rosedale on 11/17/12.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__Cloud__
|
||||
#define __interface__Cloud__
|
||||
|
||||
#include "Field.h"
|
||||
|
||||
#define PARTICLE_WORLD_SIZE 256.0
|
||||
|
||||
class Cloud {
|
||||
public:
|
||||
Cloud();
|
||||
void simulate(float deltaTime);
|
||||
void render();
|
||||
|
||||
private:
|
||||
struct Particle {
|
||||
glm::vec3 position, velocity, color;
|
||||
}* _particles;
|
||||
|
||||
unsigned int _count;
|
||||
glm::vec3 _bounds;
|
||||
Field* _field;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -172,6 +172,21 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlVelocity(int controlInd
|
|||
return glm::vec3(0); // bad index
|
||||
}
|
||||
|
||||
glm::quat ControllerScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
|
||||
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
const PalmData* palmData = getActivePalm(palmIndex);
|
||||
if (palmData) {
|
||||
switch (controlOfPalm) {
|
||||
case PALM_SPATIALCONTROL:
|
||||
return palmData->getRawRotation();
|
||||
case TIP_SPATIALCONTROL:
|
||||
return palmData->getRawRotation(); // currently the tip doesn't have a unique rotation, use the palm rotation
|
||||
}
|
||||
}
|
||||
return glm::quat(); // bad index
|
||||
}
|
||||
|
||||
glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex) const {
|
||||
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
|
|
|
@ -58,7 +58,7 @@ public slots:
|
|||
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const;
|
||||
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const;
|
||||
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const;
|
||||
|
||||
virtual glm::quat getSpatialControlRawRotation(int controlIndex) const;
|
||||
virtual void captureKeyEvents(const KeyEvent& event);
|
||||
virtual void releaseKeyEvents(const KeyEvent& event);
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ Menu::Menu() :
|
|||
#endif
|
||||
|
||||
addDisabledActionAndSeparator(editMenu, "Physics");
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, true);
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, false);
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ClickToFly);
|
||||
|
@ -238,9 +238,9 @@ Menu::Menu() :
|
|||
SLOT(setFullscreen(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true,
|
||||
appInstance,SLOT(cameraMenuChanged()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H,false,
|
||||
appInstance,SLOT(cameraMenuChanged()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
|
||||
appInstance, SLOT(cameraMenuChanged()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0,
|
||||
false,
|
||||
|
@ -266,14 +266,8 @@ Menu::Menu() :
|
|||
appInstance->getAvatar(),
|
||||
SLOT(resetSize()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::OffAxisProjection,
|
||||
0,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::TurnWithHead,
|
||||
0,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MoveWithLean, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HeadMouse, 0, false);
|
||||
|
||||
|
@ -298,7 +292,6 @@ Menu::Menu() :
|
|||
appInstance->getGlowEffect(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ParticleCloud, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, false);
|
||||
|
||||
|
@ -339,14 +332,14 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
|
||||
MenuOption::FaceshiftTCP,
|
||||
0,
|
||||
false,
|
||||
appInstance->getFaceshift(),
|
||||
SLOT(setTCPEnabled(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false);
|
||||
|
||||
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
|
||||
|
||||
|
@ -356,7 +349,7 @@ Menu::Menu() :
|
|||
true,
|
||||
appInstance->getSixenseManager(),
|
||||
SLOT(setFilter(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayLeapHands, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::VoxelDrumming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false);
|
||||
|
|
|
@ -183,7 +183,7 @@ namespace MenuOption {
|
|||
const QString DisableDeltaSending = "Disable Delta Sending";
|
||||
const QString DisableLowRes = "Disable Lower Resolution While Moving";
|
||||
const QString DisplayFrustum = "Display Frustum";
|
||||
const QString DisplayLeapHands = "Display Leap Hands";
|
||||
const QString DisplayHands = "Display Hands";
|
||||
const QString DisplayHandTargets = "Display Hand Targets";
|
||||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
const QString DontRenderVoxels = "Don't call _voxels.render()";
|
||||
|
@ -223,7 +223,6 @@ namespace MenuOption {
|
|||
const QString KillLocalVoxels = "Kill Local Voxels";
|
||||
const QString GoHome = "Go Home";
|
||||
const QString Gravity = "Use Gravity";
|
||||
const QString ParticleCloud = "Particle Cloud";
|
||||
const QString LodTools = "LOD Tools";
|
||||
const QString Log = "Log";
|
||||
const QString Login = "Login";
|
||||
|
|
|
@ -32,12 +32,7 @@ Hand::Hand(Avatar* owningAvatar) :
|
|||
_ballColor(0.0, 0.0, 0.4),
|
||||
_collisionCenter(0,0,0),
|
||||
_collisionAge(0),
|
||||
_collisionDuration(0),
|
||||
_pitchUpdate(0),
|
||||
_grabDelta(0, 0, 0),
|
||||
_grabDeltaVelocity(0, 0, 0),
|
||||
_grabStartRotation(0, 0, 0, 1),
|
||||
_grabCurrentRotation(0, 0, 0, 1)
|
||||
_collisionDuration(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -54,28 +49,6 @@ void Hand::init() {
|
|||
void Hand::reset() {
|
||||
}
|
||||
|
||||
glm::vec3 Hand::getAndResetGrabDelta() {
|
||||
const float HAND_GRAB_SCALE_DISTANCE = 2.f;
|
||||
glm::vec3 delta = _grabDelta * _owningAvatar->getScale() * HAND_GRAB_SCALE_DISTANCE;
|
||||
_grabDelta = glm::vec3(0,0,0);
|
||||
glm::quat avatarRotation = _owningAvatar->getOrientation();
|
||||
return avatarRotation * -delta;
|
||||
}
|
||||
|
||||
glm::vec3 Hand::getAndResetGrabDeltaVelocity() {
|
||||
const float HAND_GRAB_SCALE_VELOCITY = 5.f;
|
||||
glm::vec3 delta = _grabDeltaVelocity * _owningAvatar->getScale() * HAND_GRAB_SCALE_VELOCITY;
|
||||
_grabDeltaVelocity = glm::vec3(0,0,0);
|
||||
glm::quat avatarRotation = _owningAvatar->getOrientation();
|
||||
return avatarRotation * -delta;
|
||||
|
||||
}
|
||||
glm::quat Hand::getAndResetGrabRotation() {
|
||||
glm::quat diff = _grabCurrentRotation * glm::inverse(_grabStartRotation);
|
||||
_grabStartRotation = _grabCurrentRotation;
|
||||
return diff;
|
||||
}
|
||||
|
||||
void Hand::simulate(float deltaTime, bool isMine) {
|
||||
|
||||
if (_collisionAge > 0.f) {
|
||||
|
@ -98,19 +71,6 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
|
||||
_buckyBalls.grab(palm, fingerTipPosition, _owningAvatar->getOrientation(), deltaTime);
|
||||
|
||||
if (palm.getControllerButtons() & BUTTON_4) {
|
||||
_grabDelta += palm.getRawVelocity() * deltaTime;
|
||||
_grabCurrentRotation = palm.getRawRotation();
|
||||
}
|
||||
if ((palm.getLastControllerButtons() & BUTTON_4) && !(palm.getControllerButtons() & BUTTON_4)) {
|
||||
// Just ending grab, capture velocity
|
||||
_grabDeltaVelocity = palm.getRawVelocity();
|
||||
}
|
||||
if (!(palm.getLastControllerButtons() & BUTTON_4) && (palm.getControllerButtons() & BUTTON_4)) {
|
||||
// Just starting grab, capture starting rotation
|
||||
_grabStartRotation = palm.getRawRotation();
|
||||
}
|
||||
|
||||
if (palm.getControllerButtons() & BUTTON_1) {
|
||||
if (glm::length(fingerTipPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
|
||||
QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value<QColor>();
|
||||
|
@ -356,7 +316,7 @@ void Hand::render(bool isMine) {
|
|||
}
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayLeapHands)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) {
|
||||
renderLeapHands(isMine);
|
||||
}
|
||||
|
||||
|
|
|
@ -58,15 +58,6 @@ public:
|
|||
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
|
||||
const glm::vec3& getLeapFingerRootBallPosition(int ball) const { return _leapFingerRootBalls[ball].position;}
|
||||
|
||||
// Pitch from controller input to view
|
||||
const float getPitchUpdate() const { return _pitchUpdate; }
|
||||
void setPitchUpdate(float pitchUpdate) { _pitchUpdate = pitchUpdate; }
|
||||
|
||||
// Get the drag distance to move
|
||||
glm::vec3 getAndResetGrabDelta();
|
||||
glm::vec3 getAndResetGrabDeltaVelocity();
|
||||
glm::quat getAndResetGrabRotation();
|
||||
|
||||
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);
|
||||
void collideAgainstOurself();
|
||||
|
||||
|
@ -102,13 +93,6 @@ private:
|
|||
void calculateGeometry();
|
||||
|
||||
void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime);
|
||||
|
||||
float _pitchUpdate;
|
||||
|
||||
glm::vec3 _grabDelta;
|
||||
glm::vec3 _grabDeltaVelocity;
|
||||
glm::quat _grabStartRotation;
|
||||
glm::quat _grabCurrentRotation;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -310,21 +310,6 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
|
||||
updateChatCircle(deltaTime);
|
||||
|
||||
// Get any position, velocity, or rotation update from Grab Drag controller
|
||||
glm::vec3 moveFromGrab = _hand.getAndResetGrabDelta();
|
||||
if (glm::length(moveFromGrab) > EPSILON) {
|
||||
_position += moveFromGrab;
|
||||
_velocity = glm::vec3(0, 0, 0);
|
||||
}
|
||||
_velocity += _hand.getAndResetGrabDeltaVelocity();
|
||||
glm::quat deltaRotation = _hand.getAndResetGrabRotation();
|
||||
const float GRAB_CONTROLLER_TURN_SCALING = 0.5f;
|
||||
glm::vec3 euler = safeEulerAngles(deltaRotation) * GRAB_CONTROLLER_TURN_SCALING;
|
||||
// Adjust body yaw by yaw from controller
|
||||
setOrientation(glm::angleAxis(-euler.y, glm::vec3(0, 1, 0)) * getOrientation());
|
||||
// Adjust head pitch from controller
|
||||
getHead().setPitch(getHead().getPitch() - euler.x);
|
||||
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
// update avatar skeleton and simulate hand and head
|
||||
|
@ -791,38 +776,6 @@ void MyAvatar::updateThrust(float deltaTime) {
|
|||
up;
|
||||
}
|
||||
}
|
||||
// Add thrust and rotation from hand controllers
|
||||
const float THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
|
||||
const float JOYSTICK_YAW_MAG = YAW_MAG;
|
||||
const float JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5f;
|
||||
const int THRUST_CONTROLLER = 0;
|
||||
const int VIEW_CONTROLLER = 1;
|
||||
for (size_t i = 0; i < getHand().getPalms().size(); ++i) {
|
||||
PalmData& palm = getHand().getPalms()[i];
|
||||
|
||||
// If the script hasn't captured this joystick, then let the default behavior work
|
||||
if (!Application::getInstance()->getControllerScriptingInterface()->isJoystickCaptured(palm.getSixenseID())) {
|
||||
if (palm.isActive() && (palm.getSixenseID() == THRUST_CONTROLLER)) {
|
||||
if (palm.getJoystickY() != 0.f) {
|
||||
FingerData& finger = palm.getFingers()[0];
|
||||
if (finger.isActive()) {
|
||||
}
|
||||
_thrust += front * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickY() * _thrustMultiplier * deltaTime;
|
||||
}
|
||||
if (palm.getJoystickX() != 0.f) {
|
||||
_thrust += right * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickX() * _thrustMultiplier * deltaTime;
|
||||
}
|
||||
} else if (palm.isActive() && (palm.getSixenseID() == VIEW_CONTROLLER)) {
|
||||
if (palm.getJoystickX() != 0.f) {
|
||||
_bodyYawDelta -= palm.getJoystickX() * JOYSTICK_YAW_MAG * deltaTime;
|
||||
}
|
||||
if (palm.getJoystickY() != 0.f) {
|
||||
getHand().setPitchUpdate(getHand().getPitchUpdate() +
|
||||
(palm.getJoystickY() * JOYSTICK_PITCH_MAG * deltaTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update speed brake status
|
||||
const float MIN_SPEED_BRAKE_VELOCITY = _scale * 0.4f;
|
||||
|
|
|
@ -45,7 +45,6 @@ public:
|
|||
|
||||
// setters
|
||||
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
||||
void setLeanScale(float scale) { _leanScale = scale; }
|
||||
void setGravity(glm::vec3 gravity);
|
||||
|
@ -78,9 +77,6 @@ public:
|
|||
|
||||
static void sendKillAvatar();
|
||||
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
void orbit(const glm::vec3& position, int deltaX, int deltaY);
|
||||
|
||||
|
@ -94,9 +90,13 @@ public slots:
|
|||
void increaseSize();
|
||||
void decreaseSize();
|
||||
void resetSize();
|
||||
|
||||
void sendIdentityPacket();
|
||||
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
|
||||
private:
|
||||
bool _mousePressed;
|
||||
float _bodyPitchDelta;
|
||||
|
|
|
@ -67,6 +67,7 @@ class AvatarData : public NodeData {
|
|||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
||||
Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale)
|
||||
Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition)
|
||||
Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw)
|
||||
Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <QtCore/QObject>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include "EventTypes.h"
|
||||
|
||||
|
@ -37,6 +38,7 @@ public slots:
|
|||
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0;
|
||||
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0;
|
||||
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0;
|
||||
virtual glm::quat getSpatialControlRawRotation(int controlIndex) const = 0;
|
||||
|
||||
virtual void captureKeyEvents(const KeyEvent& event) = 0;
|
||||
virtual void releaseKeyEvents(const KeyEvent& event) = 0;
|
||||
|
|
|
@ -9,7 +9,10 @@
|
|||
//
|
||||
//
|
||||
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
||||
#include <OctreeConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
#include "Quat.h"
|
||||
|
||||
glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) {
|
||||
|
@ -24,6 +27,11 @@ glm::quat Quat::fromPitchYawRoll(float pitch, float yaw, float roll) {
|
|||
return glm::quat(glm::radians(glm::vec3(pitch, yaw, roll)));
|
||||
}
|
||||
|
||||
glm::quat Quat::inverse(const glm::quat& q) {
|
||||
return glm::inverse(q);
|
||||
}
|
||||
|
||||
|
||||
glm::vec3 Quat::getFront(const glm::quat& orientation) {
|
||||
return orientation * IDENTITY_FRONT;
|
||||
}
|
||||
|
@ -35,3 +43,12 @@ glm::vec3 Quat::getRight(const glm::quat& orientation) {
|
|||
glm::vec3 Quat::getUp(const glm::quat& orientation) {
|
||||
return orientation * IDENTITY_UP;
|
||||
}
|
||||
|
||||
glm::vec3 Quat::safeEulerAngles(const glm::quat& orientation) {
|
||||
return ::safeEulerAngles(orientation);
|
||||
}
|
||||
|
||||
glm::quat Quat::angleAxis(float angle, const glm::vec3& v) {
|
||||
return glm::angleAxis(angle, v);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,11 +23,12 @@ public slots:
|
|||
glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
|
||||
glm::quat fromVec3(const glm::vec3& vec3);
|
||||
glm::quat fromPitchYawRoll(float pitch, float yaw, float roll);
|
||||
glm::quat inverse(const glm::quat& q);
|
||||
glm::vec3 getFront(const glm::quat& orientation);
|
||||
glm::vec3 getRight(const glm::quat& orientation);
|
||||
glm::vec3 getUp(const glm::quat& orientation);
|
||||
glm::vec3 safeEulerAngles(const glm::quat& orientation);
|
||||
glm::quat angleAxis(float angle, const glm::vec3& v);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* defined(__hifi__Quat__) */
|
||||
|
|
|
@ -19,6 +19,16 @@ glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) {
|
|||
return v1 * f;
|
||||
}
|
||||
|
||||
glm::vec3 Vec3::multiplyQbyV(const glm::quat& q, const glm::vec3& v) {
|
||||
return q * v;
|
||||
}
|
||||
|
||||
glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) {
|
||||
return v1 + v2;
|
||||
}
|
||||
glm::vec3 Vec3::subtract(const glm::vec3& v1, const glm::vec3& v2) {
|
||||
return v1 - v2;
|
||||
}
|
||||
float Vec3::length(const glm::vec3& v) {
|
||||
return glm::length(v);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,10 @@ class Vec3 : public QObject {
|
|||
public slots:
|
||||
glm::vec3 multiply(const glm::vec3& v1, const glm::vec3& v2);
|
||||
glm::vec3 multiply(const glm::vec3& v1, float f);
|
||||
glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v);
|
||||
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2);
|
||||
glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2);
|
||||
float length(const glm::vec3& v);
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue