Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels

This commit is contained in:
Andrzej Kapolka 2014-02-10 09:48:24 -08:00
commit d48b7b8ede
35 changed files with 1351 additions and 291 deletions

View file

@ -13,7 +13,6 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/SetupHifiProject.cmake)
setup_hifi_project(${TARGET_NAME} TRUE)
# link in the shared library

View file

@ -171,7 +171,7 @@ void DomainServer::createStaticAssignmentsForTypeGivenConfigString(Assignment::T
QStringList multiConfigList = configString.split(";", QString::SkipEmptyParts);
const QString ASSIGNMENT_CONFIG_POOL_REGEX = "--pool\\s*(\\w+)";
const QString ASSIGNMENT_CONFIG_POOL_REGEX = "--pool\\s*([\\w-]+)";
QRegExp poolRegex(ASSIGNMENT_CONFIG_POOL_REGEX);
// read each config to a payload for this type of assignment

View file

@ -0,0 +1,62 @@
//
// addVoxelOnMouseClickExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 2/6/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Camera and Voxels class to implement
// clicking on a voxel and adding a new voxel on the clicked on face
//
//
function mousePressEvent(event) {
print("mousePressEvent event.x,y=" + event.x + ", " + event.y);
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Voxels.findRayIntersection(pickRay);
if (intersection.intersects) {
// Note: due to the current C++ "click on voxel" behavior, these values may be the animated color for the voxel
print("clicked on voxel.red/green/blue=" + intersection.voxel.red + ", "
+ intersection.voxel.green + ", " + intersection.voxel.blue);
print("clicked on voxel.x/y/z/s=" + intersection.voxel.x + ", "
+ intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s);
print("clicked on face=" + intersection.face);
print("clicked on distance=" + intersection.distance);
var newVoxel = {
x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z,
s: intersection.voxel.s,
red: 255,
green: 0,
blue: 255 };
if (intersection.face == "MIN_X_FACE") {
newVoxel.x -= newVoxel.s;
} else if (intersection.face == "MAX_X_FACE") {
newVoxel.x += newVoxel.s;
} else if (intersection.face == "MIN_Y_FACE") {
newVoxel.y -= newVoxel.s;
} else if (intersection.face == "MAX_Y_FACE") {
newVoxel.y += newVoxel.s;
} else if (intersection.face == "MIN_Z_FACE") {
newVoxel.z -= newVoxel.s;
} else if (intersection.face == "MAX_Z_FACE") {
newVoxel.z += newVoxel.s;
}
print("Voxels.setVoxel("+newVoxel.x + ", "
+ newVoxel.y + ", " + newVoxel.z + ", " + newVoxel.s + ", "
+ newVoxel.red + ", " + newVoxel.green + ", " + newVoxel.blue + ")" );
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
}
}
Controller.mousePressEvent.connect(mousePressEvent);
function scriptEnding() {
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -20,6 +20,7 @@ function checkController() {
var numberOfTriggers = Controller.getNumberOfTriggers();
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
var triggerToggled = false;
// this is expected for hydras
if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
@ -46,157 +47,150 @@ function checkController() {
}
}
// register the call back so it fires before each data send
Script.willSendVisualDataCallback.connect(checkController);
function printKeyEvent(eventName, event) {
print(eventName);
print(" event.key=" + event.key);
print(" event.text=" + event.text);
print(" event.isShifted=" + event.isShifted);
print(" event.isControl=" + event.isControl);
print(" event.isMeta=" + event.isMeta);
print(" event.isAlt=" + event.isAlt);
print(" event.isKeypad=" + event.isKeypad);
}
function keyPressEvent(event) {
print("keyPressEvent event.key=" + event.key);
print("keyPressEvent event.text=" + event.text);
printKeyEvent("keyPressEvent", event);
print("keyPressEvent event.isShifted=" + event.isShifted);
print("keyPressEvent event.isControl=" + event.isControl);
print("keyPressEvent event.isMeta=" + event.isMeta);
print("keyPressEvent event.isAlt=" + event.isAlt);
print("keyPressEvent event.isKeypad=" + event.isKeypad);
if (event.key == "A".charCodeAt(0)) {
if (event.text == "A") {
print("the A key was pressed");
}
if (event.key == " ".charCodeAt(0)) {
if (event.text == " ") {
print("the <space> key was pressed");
}
}
function mouseMoveEvent(event) {
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
}
function keyReleaseEvent(event) {
printKeyEvent("keyReleaseEvent", event);
function touchBeginEvent(event) {
print("touchBeginEvent event.x,y=" + event.x + ", " + event.y);
if (event.text == "A") {
print("the A key was released");
}
if (event.text == " ") {
print("the <space> key was pressed");
}
}
function touchUpdateEvent(event) {
print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y);
}
function touchEndEvent(event) {
print("touchEndEvent event.x,y=" + event.x + ", " + event.y);
}
// register the call back so it fires before each data send
Script.willSendVisualDataCallback.connect(checkController);
// Map keyPress and mouse move events to our callbacks
Controller.keyPressEvent.connect(keyPressEvent);
var KeyEvent_A = {
key: "A".charCodeAt(0),
text: "A",
isShifted: false,
isMeta: false
};
var KeyEvent_a = {
text: "a",
isShifted: false,
isMeta: false
};
var KeyEvent_a2 = {
key: "a".charCodeAt(0),
isShifted: false,
isMeta: false
};
var KeyEvent_a3 = {
text: "a"
};
var KeyEvent_A2 = {
text: "A"
};
var KeyEvent_9 = {
text: "9"
};
var KeyEvent_Num = {
text: "#"
};
var KeyEvent_At = {
text: "@"
};
var KeyEvent_MetaAt = {
text: "@",
isMeta: true
};
var KeyEvent_Up = {
text: "up"
};
var KeyEvent_Down = {
text: "down"
};
var KeyEvent_Left = {
text: "left"
};
var KeyEvent_Right = {
text: "right"
};
Controller.keyReleaseEvent.connect(keyReleaseEvent);
// prevent the A key from going through to the application
print("KeyEvent_A");
Controller.captureKeyEvents(KeyEvent_A);
print("KeyEvent_A2");
Controller.captureKeyEvents(KeyEvent_A2);
print("KeyEvent_a");
Controller.captureKeyEvents(KeyEvent_a);
print("KeyEvent_a2");
Controller.captureKeyEvents(KeyEvent_a2);
print("KeyEvent_a3");
Controller.captureKeyEvents(KeyEvent_a3);
print("KeyEvent_9");
Controller.captureKeyEvents(KeyEvent_9);
print("KeyEvent_Num");
Controller.captureKeyEvents(KeyEvent_Num);
print("KeyEvent_At");
Controller.captureKeyEvents(KeyEvent_At);
print("KeyEvent_MetaAt");
Controller.captureKeyEvents(KeyEvent_MetaAt);
print("KeyEvent_Up");
Controller.captureKeyEvents(KeyEvent_Up);
print("KeyEvent_Down");
Controller.captureKeyEvents(KeyEvent_Down);
print("KeyEvent_Left");
Controller.captureKeyEvents(KeyEvent_Left);
print("KeyEvent_Right");
Controller.captureKeyEvents(KeyEvent_Right);
Controller.captureKeyEvents({ text: "A" });
Controller.captureKeyEvents({ key: "A".charCodeAt(0) }); // same as above, just another example of how to capture the key
Controller.captureKeyEvents({ text: " " });
Controller.captureKeyEvents({ text: "@", isMeta: true });
Controller.captureKeyEvents({ text: "page up" });
Controller.captureKeyEvents({ text: "page down" });
function printMouseEvent(eventName, event) {
print(eventName);
print(" event.x,y=" + event.x + ", " + event.y);
print(" event.button=" + event.button);
print(" event.isLeftButton=" + event.isLeftButton);
print(" event.isRightButton=" + event.isRightButton);
print(" event.isMiddleButton=" + event.isMiddleButton);
print(" event.isShifted=" + event.isShifted);
print(" event.isControl=" + event.isControl);
print(" event.isMeta=" + event.isMeta);
print(" event.isAlt=" + event.isAlt);
}
function mouseMoveEvent(event) {
printMouseEvent("mouseMoveEvent", event);
}
function mousePressEvent(event) {
printMouseEvent("mousePressEvent", event);
}
function mouseReleaseEvent(event) {
printMouseEvent("mouseReleaseEvent", event);
}
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
function printTouchEvent(eventName, event) {
print(eventName);
print(" event.x,y=" + event.x + ", " + event.y);
print(" event.isPressed=" + event.isPressed);
print(" event.isMoved=" + event.isMoved);
print(" event.isStationary=" + event.isStationary);
print(" event.isReleased=" + event.isReleased);
print(" event.isShifted=" + event.isShifted);
print(" event.isControl=" + event.isControl);
print(" event.isMeta=" + event.isMeta);
print(" event.isAlt=" + event.isAlt);
for (var i = 0; i < event.points.length; i++) {
print(" event.points[" + i + "].x.y:" + event.points[i].x + ", " + event.points[i].y);
}
print(" event.radius=" + event.radius);
print(" event.isPinching=" + event.isPinching);
print(" event.isPinchOpening=" + event.isPinchOpening);
print(" event.angle=" + event.angle);
for (var i = 0; i < event.points.length; i++) {
print(" event.angles[" + i + "]:" + event.angles[i]);
}
print(" event.isRotating=" + event.isRotating);
print(" event.rotating=" + event.rotating);
}
function touchBeginEvent(event) {
printTouchEvent("touchBeginEvent", event);
}
function touchUpdateEvent(event) {
printTouchEvent("touchUpdateEvent", event);
}
function touchEndEvent(event) {
printTouchEvent("touchEndEvent", event);
}
// Map touch events to our callbacks
Controller.touchBeginEvent.connect(touchBeginEvent);
Controller.touchUpdateEvent.connect(touchUpdateEvent);
Controller.touchEndEvent.connect(touchEndEvent);
// disable the standard application for touch events
Controller.captureTouchEvents();
function wheelEvent(event) {
print("wheelEvent");
print(" event.x,y=" + event.x + ", " + event.y);
print(" event.delta=" + event.delta);
print(" event.orientation=" + event.orientation);
print(" event.isLeftButton=" + event.isLeftButton);
print(" event.isRightButton=" + event.isRightButton);
print(" event.isMiddleButton=" + event.isMiddleButton);
print(" event.isShifted=" + event.isShifted);
print(" event.isControl=" + event.isControl);
print(" event.isMeta=" + event.isMeta);
print(" event.isAlt=" + event.isAlt);
}
Controller.wheelEvent.connect(wheelEvent);
function scriptEnding() {
// re-enabled the standard application for touch events
Controller.releaseTouchEvents();
Controller.releaseKeyEvents({ text: "A" });
Controller.releaseKeyEvents({ key: "A".charCodeAt(0) }); // same as above, just another example of how to capture the key
Controller.releaseKeyEvents({ text: " " });
Controller.releaseKeyEvents({ text: "@", isMeta: true });
Controller.releaseKeyEvents({ text: "page up" });
Controller.releaseKeyEvents({ text: "page down" });
}
Script.scriptEnding.connect(scriptEnding);

170
examples/editVoxels.js Normal file
View file

@ -0,0 +1,170 @@
//
// editVoxels.js
// hifi
//
// Created by Philip Rosedale on February 8, 2014
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// Captures mouse clicks and edits voxels accordingly.
//
// click = create a new voxel on this face, same color as old
// Alt + click = delete this voxel
// shift + click = recolor this voxel
//
// Click and drag to create more new voxels in the same direction
//
function vLength(v) {
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
var key_alt = false;
var key_shift = false;
var isAdding = false;
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 };
// Create a table of the different colors you can choose
var colors = new Array();
colors[0] = { red: 237, green: 175, blue: 0 };
colors[1] = { red: 61, green: 211, blue: 72 };
colors[2] = { red: 51, green: 204, blue: 204 };
colors[3] = { red: 63, green: 169, blue: 245 };
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;
// 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 audioOptions = new AudioInjectionOptions();
function mousePressEvent(event) {
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 };
if (intersection.intersects) {
if (key_alt) {
// Delete voxel
Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
Audio.playSound(deleteSound, audioOptions);
} else if (key_shift) {
// Recolor Voxel
whichColor++;
if (whichColor == numColors) whichColor = 0;
Voxels.setVoxel(intersection.voxel.x,
intersection.voxel.y,
intersection.voxel.z,
intersection.voxel.s,
colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue);
Audio.playSound(changeColorSound, audioOptions);
} else {
// Add voxel on face
var newVoxel = {
x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z,
s: intersection.voxel.s,
red: intersection.voxel.red,
green: intersection.voxel.green,
blue: intersection.voxel.blue };
if (intersection.face == "MIN_X_FACE") {
newVoxel.x -= newVoxel.s;
} else if (intersection.face == "MAX_X_FACE") {
newVoxel.x += newVoxel.s;
} else if (intersection.face == "MIN_Y_FACE") {
newVoxel.y -= newVoxel.s;
} else if (intersection.face == "MAX_Y_FACE") {
newVoxel.y += newVoxel.s;
} else if (intersection.face == "MIN_Z_FACE") {
newVoxel.z -= newVoxel.s;
} else if (intersection.face == "MAX_Z_FACE") {
newVoxel.z += newVoxel.s;
}
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
lastVoxelPosition = { x: newVoxel.x, y: newVoxel.y, z: newVoxel.z };
lastVoxelColor = { red: newVoxel.red, green: newVoxel.green, blue: newVoxel.blue };
lastVoxelScale = newVoxel.s;
Audio.playSound(addSound, audioOptions);
dragStart = { x: event.x, y: event.y };
isAdding = true;
}
}
}
function keyPressEvent(event) {
key_alt = event.isAlt;
key_shift = event.isShifted;
}
function keyReleaseEvent(event) {
key_alt = false;
key_shift = false;
}
function mouseMoveEvent(event) {
if (isAdding) {
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);
}
}
}
function mouseReleaseEvent(event) {
isAdding = false;
}
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
function scriptEnding() {
}
Script.scriptEnding.connect(scriptEnding);

122
examples/lookAtExample.js Normal file
View file

@ -0,0 +1,122 @@
//
// lookAtExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 2/6/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Camera class's lookAt(), keepLookingAt(), and stopLookingAt()
// features.
//
// To use the script, click on a voxel, and the camera will switch into independent mode and fix it's lookAt on the point
// on the face of the voxel that you clicked. Click again and it will stop looking at that point. While in this fixed mode
// you can use the arrow keys to change the position of the camera.
//
//
var lookingAtSomething = false;
var oldMode = Camera.getMode();
function cancelLookAt() {
if (lookingAtSomething) {
lookingAtSomething = false;
Camera.stopLooking();
Camera.setMode(oldMode);
releaseMovementKeys();
}
}
function captureMovementKeys() {
Controller.captureKeyEvents({ text: "up" }); // Z-
Controller.captureKeyEvents({ text: "down" }); // Z+
Controller.captureKeyEvents({ text: "UP" }); // Y+
Controller.captureKeyEvents({ text: "DOWN" }); // Y-
Controller.captureKeyEvents({ text: "left" }); // X+
Controller.captureKeyEvents({ text: "right" }); // X-
}
function releaseMovementKeys() {
Controller.releaseKeyEvents({ text: "up" }); // Z-
Controller.releaseKeyEvents({ text: "down" }); // Z+
Controller.releaseKeyEvents({ text: "UP" }); // Y+
Controller.releaseKeyEvents({ text: "DOWN" }); // Y-
Controller.releaseKeyEvents({ text: "left" }); // X+
Controller.releaseKeyEvents({ text: "right" }); // X-
}
var cameraPosition = Camera.getPosition();
function moveCamera() {
if (lookingAtSomething) {
Camera.setPosition(cameraPosition);
}
}
Script.willSendVisualDataCallback.connect(moveCamera);
function mousePressEvent(event) {
if (lookingAtSomething) {
cancelLookAt();
} else {
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Voxels.findRayIntersection(pickRay);
if (intersection.intersects) {
// remember the old mode we were in
oldMode = Camera.getMode();
print("looking at intersection point: " + intersection.intersection.x + ", "
+ intersection.intersection.y + ", " + intersection.intersection.z);
// switch to independent mode
Camera.setMode("independent");
// tell the camera to fix it's look at on the point we clicked
Camera.keepLookingAt(intersection.intersection);
// keep track of the fact that we're in this looking at mode
lookingAtSomething = true;
captureMovementKeys();
cameraPosition = Camera.getPosition();
}
}
}
Controller.mousePressEvent.connect(mousePressEvent);
function keyPressEvent(event) {
if (lookingAtSomething) {
if (event.text == "ESC") {
cancelLookAt();
}
var MOVE_DELTA = 0.5;
if (event.text == "UP" && !event.isShifted) {
cameraPosition.z -= MOVE_DELTA;
}
if (event.text == "DOWN" && !event.isShifted) {
cameraPosition.z += MOVE_DELTA;
}
if (event.text == "LEFT" && !event.isShifted) {
cameraPosition.x += MOVE_DELTA;
}
if (event.text == "RIGHT" && !event.isShifted) {
cameraPosition.x -= MOVE_DELTA;
}
if (event.text == "UP" && event.isShifted) {
cameraPosition.y += MOVE_DELTA;
}
if (event.text == "DOWN" && event.isShifted) {
cameraPosition.y -= MOVE_DELTA;
}
}
}
Controller.keyPressEvent.connect(keyPressEvent);
function scriptEnding() {
cancelLookAt();
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -0,0 +1,120 @@
//
// multitouchExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 2/9/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Controller class's multi-touch features
//
// When this script is running:
// * Four finger rotate gesture will rotate your avatar.
// * Three finger swipe up/down will adjust the pitch of your avatars head.
//
var lastX = 0;
var lastY = 0;
var lastAngle = 0;
var yawFromMultiTouch = 0;
var pitchFromMultiTouch = 0;
var wantDebugging = false;
var ROTATE_YAW_SCALE = 0.15;
var MOUSE_PITCH_SCALE = -12.5;
var FIXED_MOUSE_TIMESTEP = 0.016;
var ROTATE_TOUCH_POINTS = 4;
var PITCH_TOUCH_POINTS = 3;
function printTouchEvent(eventName, event) {
print(eventName);
print(" event.x,y=" + event.x + ", " + event.y);
print(" event.isPressed=" + event.isPressed);
print(" event.isMoved=" + event.isMoved);
print(" event.isStationary=" + event.isStationary);
print(" event.isReleased=" + event.isReleased);
print(" event.isShifted=" + event.isShifted);
print(" event.isControl=" + event.isControl);
print(" event.isMeta=" + event.isMeta);
print(" event.isAlt=" + event.isAlt);
print(" event.touchPoints=" + event.touchPoints);
for (var i = 0; i < event.points.length; i++) {
print(" event.points[" + i + "].x.y:" + event.points[i].x + ", " + event.points[i].y);
}
print(" event.radius=" + event.radius);
print(" event.isPinching=" + event.isPinching);
print(" event.isPinchOpening=" + event.isPinchOpening);
print(" event.angle=" + event.angle);
print(" event.deltaAngle=" + event.deltaAngle);
for (var i = 0; i < event.points.length; i++) {
print(" event.angles[" + i + "]:" + event.angles[i]);
}
print(" event.isRotating=" + event.isRotating);
print(" event.rotating=" + event.rotating);
}
function touchBeginEvent(event) {
printTouchEvent("touchBeginEvent", event);
lastX = event.x;
lastY = event.y;
}
function touchUpdateEvent(event) {
printTouchEvent("touchUpdateEvent", event);
if (event.isRotating && event.touchPoints == ROTATE_TOUCH_POINTS) {
// it's possible for the multitouch rotate gesture to generate angle changes which are faster than comfortable to
// view, so we will scale this change in angle to make it more comfortable
var scaledRotate = event.deltaAngle * ROTATE_YAW_SCALE;
print(">>> event.deltaAngle=" + event.deltaAngle);
print(">>> scaledRotate=" + scaledRotate);
yawFromMultiTouch += scaledRotate;
}
if (event.touchPoints == PITCH_TOUCH_POINTS) {
pitchFromMultiTouch += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP);
}
lastX = event.x;
lastY = event.y;
lastAngle = event.angle;
}
function touchEndEvent(event) {
printTouchEvent("touchEndEvent", event);
}
// Map touch events to our callbacks
Controller.touchBeginEvent.connect(touchBeginEvent);
Controller.touchUpdateEvent.connect(touchUpdateEvent);
Controller.touchEndEvent.connect(touchEndEvent);
function update() {
// rotate body yaw for yaw received from multitouch rotate
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMultiTouch, z: 0 } ));
if (wantDebugging) {
print("changing orientation"
+ " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + ","
+ MyAvatar.orientation.z + "," + MyAvatar.orientation.w
+ " newOrientation="+newOrientation.x + "," + newOrientation.y + "," + newOrientation.z + "," + newOrientation.w);
}
MyAvatar.orientation = newOrientation;
yawFromMultiTouch = 0;
// apply pitch from mouse
var newPitch = MyAvatar.headPitch + pitchFromMultiTouch;
if (wantDebugging) {
print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch);
}
MyAvatar.headPitch = newPitch;
pitchFromMultiTouch = 0;
}
Script.willSendVisualDataCallback.connect(update);
function scriptEnding() {
// handle any shutdown logic here
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -4,6 +4,7 @@
//
// This sample script moves a voxel around like a bird and sometimes makes tweeting noises
//
function vLength(v) {
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
@ -15,20 +16,24 @@ function randVector(a, b) {
var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) };
return rval;
}
function vMinus(a, b) {
var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
return rval;
}
function vPlus(a, b) {
var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
return rval;
}
function vCopy(a, b) {
a.x = b.x;
a.y = b.y;
a.z = b.z;
return;
}
// Returns a vector which is fraction of the way between a and b
function vInterpolate(a, b, fraction) {
var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction };
@ -62,6 +67,7 @@ if (which < 0.2) {
size = 0.15;
}
var startTimeInSeconds = new Date().getTime() / 1000;
var birdLifetime = 20; // lifetime of the bird in seconds!
@ -72,15 +78,19 @@ var frame = 0;
var moving = false;
var tweeting = 0;
var moved = false;
var CHANCE_OF_MOVING = 0.05;
var CHANCE_OF_MOVING = 0.00;
var CHANCE_OF_FLAPPING = 0.05;
var CHANCE_OF_TWEETING = 0.05;
var START_HEIGHT_ABOVE_ME = 1.5;
var BIRD_GRAVITY = -0.1;
var BIRD_FLAP = 1.0;
var myPosition = MyAvatar.position;
var properties = {
lifetime: birdLifetime,
position: { x: myPosition.x, y: myPosition.y + START_HEIGHT_ABOVE_ME, z: myPosition.z },
velocity: { x: 0, y: 0, z: 0 },
gravity: { x: 0, y: 0, z: 0 },
velocity: { x: 0, y: Math.random() * BIRD_FLAP, z: 0 },
gravity: { x: 0, y: BIRD_GRAVITY, z: 0 },
radius : 0.1,
color: { red: 0,
green: 255,
@ -94,6 +104,7 @@ function moveBird() {
// check to see if we've been running long enough that our bird is dead
var nowTimeInSeconds = new Date().getTime() / 1000;
if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) {
print("our bird is dying, stop our script");
Script.stop();
return;
@ -115,11 +126,22 @@ function moveBird() {
} else {
tweeting -= 1;
}
if (Math.random() < CHANCE_OF_FLAPPING) {
// Add a little upward impulse to our bird
// TODO: Get velocity
//
var newProperties = {
velocity: { x:0.0, y: Math.random() * BIRD_FLAP, z: 0.0 }
};
Particles.editParticle(particleID, newProperties);
print("flap!");
}
// Moving behavior
if (moving == false) {
if (Math.random() < CHANCE_OF_MOVING) {
targetPosition = randVector(- range, range);
targetPosition = randVector(-range, range);
targetPosition = vPlus(targetPosition, myPosition);
if (targetPosition.x < 0) {
targetPosition.x = 0;
}
@ -170,5 +192,6 @@ function moveBird() {
}
}
}
// register the call back so it fires before each data send
Script.willSendVisualDataCallback.connect(moveBird);

View file

@ -0,0 +1,37 @@
//
// rayPickExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 2/6/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Camera class
//
//
function mouseMoveEvent(event) {
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
var pickRay = Camera.computePickRay(event.x, event.y);
print("called Camera.computePickRay()");
print("computePickRay origin=" + pickRay.origin.x + ", " + pickRay.origin.y + ", " + pickRay.origin.z);
print("computePickRay direction=" + pickRay.direction.x + ", " + pickRay.direction.y + ", " + pickRay.direction.z);
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Voxels.findRayIntersection(pickRay);
if (intersection.intersects) {
print("intersection voxel.red/green/blue=" + intersection.voxel.red + ", "
+ intersection.voxel.green + ", " + intersection.voxel.blue);
print("intersection voxel.x/y/z/s=" + intersection.voxel.x + ", "
+ intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s);
print("intersection face=" + intersection.face);
print("intersection distance=" + intersection.distance);
print("intersection intersection.x/y/z=" + intersection.intersection.x + ", "
+ intersection.intersection.y + ", " + intersection.intersection.z);
}
}
Controller.mouseMoveEvent.connect(mouseMoveEvent);
function scriptEnding() {
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -135,7 +135,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_isTouchPressed(false),
_mousePressed(false),
_isHoverVoxel(false),
_isHoverVoxelSounding(false),
_mouseVoxelScale(1.0f / 1024.0f),
_mouseVoxelScaleInitialized(false),
_justEditedVoxel(false),
@ -448,7 +447,7 @@ void Application::paintGL() {
_myCamera.setTargetRotation(_myAvatar->getHead().getOrientation());
} else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay
_myCamera.setTightness(0.0f); // In first person, camera follows (untweaked) head exactly without delay
_myCamera.setTargetPosition(_myAvatar->getHead().calculateAverageEyePosition());
_myCamera.setTargetRotation(_myAvatar->getHead().getCameraOrientation());
@ -1199,7 +1198,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
return;
}
if (_isHoverVoxel) {
_myAvatar->orbit(getMouseVoxelWorldCoordinates(_hoverVoxel), deltaX, deltaY);
//_myAvatar->orbit(getMouseVoxelWorldCoordinates(_hoverVoxel), deltaX, deltaY);
return;
}
}
@ -1219,11 +1218,6 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
}
}
const bool MAKE_SOUND_ON_VOXEL_HOVER = false;
const bool MAKE_SOUND_ON_VOXEL_CLICK = true;
const float HOVER_VOXEL_FREQUENCY = 7040.f;
const float HOVER_VOXEL_DECAY = 0.999f;
void Application::mousePressEvent(QMouseEvent* event) {
_controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts
@ -1263,37 +1257,6 @@ void Application::mousePressEvent(QMouseEvent* event) {
pasteVoxels();
}
if (!Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode) &&
MAKE_SOUND_ON_VOXEL_CLICK && _isHoverVoxel && !_isHoverVoxelSounding) {
_hoverVoxelOriginalColor[0] = _hoverVoxel.red;
_hoverVoxelOriginalColor[1] = _hoverVoxel.green;
_hoverVoxelOriginalColor[2] = _hoverVoxel.blue;
_hoverVoxelOriginalColor[3] = 1;
const float RED_CLICK_FREQUENCY = 1000.f;
const float GREEN_CLICK_FREQUENCY = 1250.f;
const float BLUE_CLICK_FREQUENCY = 1330.f;
const float MIDDLE_A_FREQUENCY = 440.f;
float frequency = MIDDLE_A_FREQUENCY +
(_hoverVoxel.red / 255.f * RED_CLICK_FREQUENCY +
_hoverVoxel.green / 255.f * GREEN_CLICK_FREQUENCY +
_hoverVoxel.blue / 255.f * BLUE_CLICK_FREQUENCY) / 3.f;
_audio.startCollisionSound(1.0, frequency, 0.0, HOVER_VOXEL_DECAY, false);
_isHoverVoxelSounding = true;
const float PERCENTAGE_TO_MOVE_TOWARD = 0.90f;
glm::vec3 newTarget = getMouseVoxelWorldCoordinates(_hoverVoxel);
glm::vec3 myPosition = _myAvatar->getPosition();
// If there is not an action tool set (add, delete, color), move to this voxel
if (Menu::getInstance()->isOptionChecked(MenuOption::ClickToFly) &&
!(Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) ||
Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode) ||
Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode))) {
_myAvatar->setMoveTarget(myPosition + (newTarget - myPosition) * PERCENTAGE_TO_MOVE_TOWARD);
}
}
} else if (event->button() == Qt::RightButton && Menu::getInstance()->isVoxelModeActionChecked()) {
deleteVoxelUnderCursor();
}
@ -1324,7 +1287,9 @@ void Application::mouseReleaseEvent(QMouseEvent* event) {
}
void Application::touchUpdateEvent(QTouchEvent* event) {
_controllerScriptingInterface.emitTouchUpdateEvent(event); // send events to any registered scripts
TouchEvent thisEvent(*event, _lastTouchEvent);
_controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent;
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isTouchCaptured()) {
@ -1355,8 +1320,10 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
}
void Application::touchBeginEvent(QTouchEvent* event) {
_controllerScriptingInterface.emitTouchBeginEvent(event); // send events to any registered scripts
TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event
_controllerScriptingInterface.emitTouchBeginEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update
touchUpdateEvent(event);
// if one of our scripts have asked to capture this event, then stop processing it
@ -1371,7 +1338,9 @@ void Application::touchBeginEvent(QTouchEvent* event) {
}
void Application::touchEndEvent(QTouchEvent* event) {
_controllerScriptingInterface.emitTouchEndEvent(event); // send events to any registered scripts
TouchEvent thisEvent(*event, _lastTouchEvent);
_controllerScriptingInterface.emitTouchEndEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent;
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isTouchCaptured()) {
@ -2039,45 +2008,9 @@ void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& f
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels()");
// If we have clicked on a voxel, update it's color
if (_isHoverVoxelSounding) {
VoxelTreeElement* hoveredNode = _voxels.getVoxelAt(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s);
if (hoveredNode) {
float bright = _audio.getCollisionSoundMagnitude();
nodeColor clickColor = { 255 * bright + _hoverVoxelOriginalColor[0] * (1.f - bright),
_hoverVoxelOriginalColor[1] * (1.f - bright),
_hoverVoxelOriginalColor[2] * (1.f - bright), 1 };
hoveredNode->setColor(clickColor);
if (bright < 0.01f) {
hoveredNode->setColor(_hoverVoxelOriginalColor);
_isHoverVoxelSounding = false;
}
} else {
// Voxel is not found, clear all
_isHoverVoxelSounding = false;
_isHoverVoxel = false;
}
} else {
// Check for a new hover voxel
glm::vec4 oldVoxel(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s);
// only do this work if MAKE_SOUND_ON_VOXEL_HOVER or MAKE_SOUND_ON_VOXEL_CLICK is enabled,
// and make sure the tree is not already busy... because otherwise you'll have to wait.
if (!_mousePressed) {
{
PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()");
_isHoverVoxel = _voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _hoverVoxel, distance, face);
}
if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel &&
glm::vec4(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s) != oldVoxel) {
_hoverVoxelOriginalColor[0] = _hoverVoxel.red;
_hoverVoxelOriginalColor[1] = _hoverVoxel.green;
_hoverVoxelOriginalColor[2] = _hoverVoxel.blue;
_hoverVoxelOriginalColor[3] = 1;
_audio.startCollisionSound(1.0, HOVER_VOXEL_FREQUENCY * _hoverVoxel.s * TREE_SCALE, 0.0, HOVER_VOXEL_DECAY, false);
_isHoverVoxelSounding = true;
}
}
if (!_mousePressed) {
PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()");
_isHoverVoxel = _voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _hoverVoxel, distance, face);
}
}
@ -4101,6 +4034,7 @@ void Application::loadScript(const QString& fileNameString) {
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
// we can use the same ones from the application.
scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
scriptEngine->getVoxelsScriptingInterface()->setVoxelTree(_voxels.getTree());
scriptEngine->getParticlesScriptingInterface()->setPacketSender(&_particleEditSender);
scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree());

View file

@ -419,8 +419,6 @@ private:
VoxelDetail _hoverVoxel; // Stuff about the voxel I am hovering or clicking
bool _isHoverVoxel;
bool _isHoverVoxelSounding;
nodeColor _hoverVoxelOriginalColor;
VoxelDetail _mouseVoxel; // details of the voxel to be edited
float _mouseVoxelScale; // the scale for adding/removing voxels
@ -490,6 +488,8 @@ private:
void displayUpdateDialog();
bool shouldSkipVersion(QString latestVersion);
void takeSnapshot();
TouchEvent _lastTouchEvent;
};
#endif /* defined(__interface__Application__) */

View file

@ -65,6 +65,10 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p
_measuredJitter(0),
_jitterBufferSamples(initialJitterBufferSamples),
_lastInputLoudness(0),
_averageInputLoudness(0),
_noiseGateOpen(false),
_noiseGateEnabled(true),
_noiseGateFramesToClose(0),
_lastVelocity(0),
_lastAcceleration(0),
_totalPacketsReceived(0),
@ -348,12 +352,40 @@ void Audio::handleAudioInput() {
_inputFormat, _desiredInputFormat);
float loudness = 0;
float thisSample = 0;
int samplesOverNoiseGate = 0;
const float NOISE_GATE_HEIGHT = 3.f;
const int NOISE_GATE_WIDTH = 5;
const int NOISE_GATE_CLOSE_FRAME_DELAY = 30;
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
loudness += fabsf(monoAudioSamples[i]);
thisSample = fabsf(monoAudioSamples[i]);
loudness += thisSample;
// Noise Reduction: Count peaks above the average loudness
if (thisSample > (_averageInputLoudness * 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;
if (_noiseGateEnabled) {
if (samplesOverNoiseGate > NOISE_GATE_WIDTH) {
_noiseGateOpen = true;
_noiseGateFramesToClose = NOISE_GATE_CLOSE_FRAME_DELAY;
} else {
if (--_noiseGateFramesToClose == 0) {
_noiseGateOpen = false;
}
}
if (!_noiseGateOpen) {
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
monoAudioSamples[i] = 0;
}
}
}
// add input data just written to the scope
QMetaObject::invokeMethod(_scope, "addSamples", Qt::QueuedConnection,
@ -524,6 +556,10 @@ void Audio::toggleMute() {
muteToggled();
}
void Audio::toggleAudioNoiseReduction() {
_noiseGateEnabled = !_noiseGateEnabled;
}
void Audio::render(int screenWidth, int screenHeight) {
if (_audioInput && _audioOutput) {
glLineWidth(2.0);

View file

@ -45,7 +45,9 @@ public:
void render(int screenWidth, int screenHeight);
float getLastInputLoudness() const { return _lastInputLoudness; }
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _averageInputLoudness, 0.f); }
void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; }
void setLastAcceleration(const glm::vec3 lastAcceleration) { _lastAcceleration = lastAcceleration; }
void setLastVelocity(const glm::vec3 lastVelocity) { _lastVelocity = lastVelocity; }
@ -73,6 +75,7 @@ public slots:
void handleAudioInput();
void reset();
void toggleMute();
void toggleAudioNoiseReduction();
virtual void handleAudioByteArray(const QByteArray& audioByteArray);
@ -106,6 +109,10 @@ private:
float _measuredJitter;
int16_t _jitterBufferSamples;
float _lastInputLoudness;
float _averageInputLoudness;
bool _noiseGateOpen;
bool _noiseGateEnabled;
int _noiseGateFramesToClose;
glm::vec3 _lastVelocity;
glm::vec3 _lastAcceleration;
int _totalPacketsReceived;

View file

@ -58,7 +58,9 @@ Camera::Camera() :
_modeShift(1.0f),
_linearModeShift(0.0f),
_modeShiftRate(1.0f),
_scale(1.0f)
_scale(1.0f),
_lookingAt(0.0f, 0.0f, 0.0f),
_isKeepLookingAt(false)
{
}
@ -94,8 +96,12 @@ void Camera::updateFollowMode(float deltaTime) {
t = 1.0;
}
// Update position and rotation, setting directly if tightness is 0.0
// handle keepLookingAt
if (_isKeepLookingAt) {
lookAt(_lookingAt);
}
// Update position and rotation, setting directly if tightness is 0.0
if (_needsToInitialize || (_tightness == 0.0f)) {
_rotation = _targetRotation;
_idealPosition = _targetPosition + _scale * (_rotation * glm::vec3(0.0f, _upShift, _distance));
@ -155,6 +161,15 @@ void Camera::setMode(CameraMode m) {
}
}
void Camera::setTargetPosition(const glm::vec3& t) {
_targetPosition = t;
// handle keepLookingAt
if (_isKeepLookingAt) {
lookAt(_lookingAt);
}
}
void Camera::setTargetRotation( const glm::quat& targetRotation ) {
_targetRotation = targetRotation;
@ -222,6 +237,19 @@ void Camera::setFrustumWasReshaped() {
_frustumNeedsReshape = false;
}
void Camera::lookAt(const glm::vec3& lookAt) {
glm::vec3 up = IDENTITY_UP;
glm::mat4 lookAtMatrix = glm::lookAt(_targetPosition, lookAt, up);
glm::quat rotation = glm::quat_cast(lookAtMatrix);
rotation.w = -rotation.w; // Rosedale approved
setTargetRotation(rotation);
}
void Camera::keepLookingAt(const glm::vec3& point) {
lookAt(point);
_isKeepLookingAt = true;
_lookingAt = point;
}
CameraScriptableObject::CameraScriptableObject(Camera* camera, ViewFrustum* viewFrustum) :
_camera(camera), _viewFrustum(viewFrustum)

View file

@ -10,6 +10,7 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <RegisteredMetaTypes.h>
#include <ViewFrustum.h>
@ -37,7 +38,7 @@ public:
void setUpShift(float u) { _upShift = u; }
void setDistance(float d) { _distance = d; }
void setPosition(const glm::vec3& p) { _position = p; }
void setTargetPosition(const glm::vec3& t) { _targetPosition = t; }
void setTargetPosition(const glm::vec3& t);
void setTightness(float t) { _tightness = t; }
void setTargetRotation(const glm::quat& rotation);
@ -68,6 +69,17 @@ public:
bool getFrustumNeedsReshape() const; // call to find out if the view frustum needs to be reshaped
void setFrustumWasReshaped(); // call this after reshaping the view frustum.
// These only work on independent cameras
/// one time change to what the camera is looking at
void lookAt(const glm::vec3& value);
/// fix what the camera is looking at, and keep the camera looking at this even if position changes
void keepLookingAt(const glm::vec3& value);
/// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from
/// continuing to update it's orientation to keep looking at the item
void stopLooking() { _isKeepLookingAt = false; }
private:
@ -99,6 +111,9 @@ private:
float _linearModeShift;
float _modeShiftRate;
float _scale;
glm::vec3 _lookingAt;
bool _isKeepLookingAt;
void updateFollowMode(float deltaTime);
};
@ -119,6 +134,17 @@ public slots:
void setOrientation(const glm::quat& value) { _camera->setTargetRotation(value); }
glm::quat getOrientation() const { return _camera->getRotation(); }
// These only work on independent cameras
/// one time change to what the camera is looking at
void lookAt(const glm::vec3& value) { _camera->lookAt(value);}
/// fix what the camera is looking at, and keep the camera looking at this even if position changes
void keepLookingAt(const glm::vec3& value) { _camera->keepLookingAt(value);}
/// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from
/// continuing to update it's orientation to keep looking at the item
void stopLooking() { _camera->stopLooking();}
PickRay computePickRay(float x, float y);
private:

View file

@ -28,9 +28,10 @@ public:
void emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(MouseEvent(*event)); }
void emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(MouseEvent(*event)); }
void emitTouchBeginEvent(QTouchEvent* event) { emit touchBeginEvent(*event); }
void emitTouchEndEvent(QTouchEvent* event) { emit touchEndEvent(*event); }
void emitTouchUpdateEvent(QTouchEvent* event) { emit touchUpdateEvent(*event); }
void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
void emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }
void emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); }
void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); }
bool isKeyCaptured(QKeyEvent* event) const;

View file

@ -457,6 +457,11 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P);
QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools");
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction,
0,
true,
appInstance->getAudio(),
SLOT(toggleAudioNoiseReduction()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio);
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio);
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteAudio,

View file

@ -187,6 +187,7 @@ namespace MenuOption {
const QString Enable3DTVMode = "Enable 3DTV Mode";
const QString EnableOcclusionCulling = "Enable Occlusion Culling";
const QString EnableVoxelPacketCompression = "Enable Voxel Packet Compression";
const QString AudioNoiseReduction = "Audio Noise Reduction";
const QString EchoServerAudio = "Echo Server Audio";
const QString EchoLocalAudio = "Echo Local Audio";
const QString MuteAudio = "Mute Microphone";

View file

@ -82,7 +82,6 @@ public:
//getters
bool isInitialized() const { return _initialized; }
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
float getHeadYawRate() const { return _head.yawRate; }
glm::vec3 getChestPosition() const;
float getScale() const { return _scale; }
const glm::vec3& getVelocity() const { return _velocity; }

View file

@ -59,15 +59,15 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) *
joint.preTransform * glm::mat4_cast(joint.preRotation)));
state.rotation = glm::angleAxis(-_owningHead->getRoll(), glm::normalize(inverse * axes[2])) *
glm::angleAxis(_owningHead->getYaw(), glm::normalize(inverse * axes[1])) *
glm::angleAxis(-_owningHead->getPitch(), glm::normalize(inverse * axes[0])) * joint.rotation;
glm::angleAxis(_owningHead->getTweakedYaw(), glm::normalize(inverse * axes[1])) *
glm::angleAxis(-_owningHead->getTweakedPitch(), glm::normalize(inverse * axes[0])) * joint.rotation;
}
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
// likewise with the eye joints
glm::mat4 inverse = glm::inverse(parentState.transform * glm::translate(state.translation) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getOrientation() * IDENTITY_FRONT, 0.0f));
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getTweakedOrientation() * IDENTITY_FRONT, 0.0f));
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() +
_owningHead->getSaccade() - _translation, 1.0f));
glm::quat between = rotationBetween(front, lookAt);

View file

@ -18,7 +18,6 @@ using namespace std;
Head::Head(Avatar* owningAvatar) :
HeadData((AvatarData*)owningAvatar),
yawRate(0.0f),
_returnHeadToCenter(false),
_position(0.0f, 0.0f, 0.0f),
_rotation(0.0f, 0.0f, 0.0f),
@ -37,6 +36,9 @@ Head::Head(Avatar* owningAvatar) :
_leftEyeBlinkVelocity(0.0f),
_rightEyeBlinkVelocity(0.0f),
_timeWithoutTalking(0.0f),
_tweakedPitch(0.f),
_tweakedYaw(0.f),
_tweakedRoll(0.f),
_isCameraMoving(false),
_faceModel(this)
{
@ -186,9 +188,14 @@ glm::quat Head::getOrientation() const {
return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll)));
}
glm::quat Head::getTweakedOrientation() const {
return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(getTweakedPitch(), getTweakedYaw(), getTweakedRoll() )));
}
glm::quat Head::getCameraOrientation () const {
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
return owningAvatar->getWorldAlignedOrientation();
return owningAvatar->getWorldAlignedOrientation()
* glm::quat(glm::radians(glm::vec3(_pitch, 0.f, 0.0f)));
}
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {
@ -200,6 +207,18 @@ glm::vec3 Head::getScalePivot() const {
return _faceModel.isActive() ? _faceModel.getTranslation() : _position;
}
float Head::getTweakedYaw() const {
return glm::clamp(_yaw + _tweakedYaw, MIN_HEAD_YAW, MAX_HEAD_YAW);
}
float Head::getTweakedPitch() const {
return glm::clamp(_pitch + _tweakedPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH);
}
float Head::getTweakedRoll() const {
return glm::clamp(_roll + _tweakedRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
}
void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
Application::getInstance()->getGlowEffect()->begin();

View file

@ -13,7 +13,7 @@
#include <SharedUtil.h>
#include <AvatarData.h>
#include <HeadData.h>
#include <VoxelConstants.h>
@ -47,6 +47,7 @@ public:
void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; }
glm::quat getOrientation() const;
glm::quat getTweakedOrientation() const;
glm::quat getCameraOrientation () const;
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
void setAngularVelocity(glm::vec3 angularVelocity) { _angularVelocity = angularVelocity; }
@ -70,9 +71,15 @@ public:
/// Returns the point about which scaling occurs.
glm::vec3 getScalePivot() const;
float yawRate;
void tweakPitch(float pitch) { _tweakedPitch = pitch; }
void tweakYaw(float yaw) { _tweakedYaw = yaw; }
void tweakRoll(float roll) { _tweakedRoll = roll; }
float getTweakedPitch() const;
float getTweakedYaw() const;
float getTweakedRoll() const;
private:
// disallow copies of the Head, copy of owning Avatar is disallowed too
Head(const Head&);
@ -96,6 +103,12 @@ private:
float _leftEyeBlinkVelocity;
float _rightEyeBlinkVelocity;
float _timeWithoutTalking;
// tweaked angles affect the rendered head, but not the camera
float _tweakedPitch;
float _tweakedYaw;
float _tweakedRoll;
bool _isCameraMoving;
FaceModel _faceModel;

View file

@ -396,9 +396,9 @@ void MyAvatar::updateFromGyros(bool turnWithHead) {
const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f;
const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f;
const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f;
_head.setPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
_head.setYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
_head.setRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
_head.tweakPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
_head.tweakYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
_head.tweakRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
// Update torso lean distance based on accelerometer data
const float TORSO_LENGTH = 0.5f;

View file

@ -107,7 +107,6 @@ QByteArray AvatarData::toByteArray() {
destinationBuffer += sizeof(_headData->_lookAtPosition);
// Instantaneous audio loudness (used to drive facial animation)
//destinationBuffer += packFloatToByte(destinationBuffer, std::min(MAX_AUDIO_LOUDNESS, _audioLoudness), MAX_AUDIO_LOUDNESS);
memcpy(destinationBuffer, &_headData->_audioLoudness, sizeof(float));
destinationBuffer += sizeof(float);
@ -215,7 +214,6 @@ int AvatarData::parseData(const QByteArray& packet) {
sourceBuffer += sizeof(_headData->_lookAtPosition);
// Instantaneous audio loudness (used to drive facial animation)
//sourceBuffer += unpackFloatFromByte(sourceBuffer, _audioLoudness, MAX_AUDIO_LOUDNESS);
memcpy(&_headData->_audioLoudness, sourceBuffer, sizeof(float));
sourceBuffer += sizeof(float);

View file

@ -9,18 +9,29 @@
// scripting engine
#include <QDebug>
#include <RegisteredMetaTypes.h>
#include "EventTypes.h"
KeyEvent::KeyEvent() {
key = 0;
text = QString("");
isShifted = false;
isMeta = false;
isControl = false;
isValid = false;
void registerEventTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, keyEventToScriptValue, keyEventFromScriptValue);
qScriptRegisterMetaType(engine, mouseEventToScriptValue, mouseEventFromScriptValue);
qScriptRegisterMetaType(engine, touchEventToScriptValue, touchEventFromScriptValue);
qScriptRegisterMetaType(engine, wheelEventToScriptValue, wheelEventFromScriptValue);
}
KeyEvent::KeyEvent() :
key(0),
text(""),
isShifted(false),
isControl(false),
isMeta(false),
isAlt(false),
isKeypad(false),
isValid(false)
{
};
KeyEvent::KeyEvent(const QKeyEvent& event) {
key = event.key();
@ -73,43 +84,34 @@ KeyEvent::KeyEvent(const QKeyEvent& event) {
text = "DELETE";
} else if (key == Qt::Key_Backspace) {
text = "BACKSPACE";
} else if (key == Qt::Key_Shift) {
text = "SHIFT";
} else if (key == Qt::Key_Alt) {
text = "ALT";
} else if (key == Qt::Key_Control) {
text = "CONTROL";
} else if (key == Qt::Key_Meta) {
text = "META";
} else if (key == Qt::Key_PageDown) {
text = "PAGE DOWN";
} else if (key == Qt::Key_PageUp) {
text = "PAGE UP";
} else if (key == Qt::Key_Home) {
text = "HOME";
} else if (key == Qt::Key_End) {
text = "END";
} else if (key == Qt::Key_Help) {
text = "HELP";
}
}
MouseEvent::MouseEvent(const QMouseEvent& event) {
x = event.x();
y = event.y();
}
TouchEvent::TouchEvent(const QTouchEvent& event) {
// convert the touch points into an average
const QList<QTouchEvent::TouchPoint>& tPoints = event.touchPoints();
float touchAvgX = 0.0f;
float touchAvgY = 0.0f;
int numTouches = tPoints.count();
if (numTouches > 1) {
for (int i = 0; i < numTouches; ++i) {
touchAvgX += tPoints[i].pos().x();
touchAvgY += tPoints[i].pos().y();
}
touchAvgX /= (float)(numTouches);
touchAvgY /= (float)(numTouches);
}
x = touchAvgX;
y = touchAvgY;
}
WheelEvent::WheelEvent(const QWheelEvent& event) {
x = event.x();
y = event.y();
}
void registerEventTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, keyEventToScriptValue, keyEventFromScriptValue);
qScriptRegisterMetaType(engine, mouseEventToScriptValue, mouseEventFromScriptValue);
qScriptRegisterMetaType(engine, touchEventToScriptValue, touchEventFromScriptValue);
qScriptRegisterMetaType(engine, wheelEventToScriptValue, wheelEventFromScriptValue);
bool KeyEvent::operator==(const KeyEvent& other) const {
return other.key == key
&& other.isShifted == isShifted
&& other.isControl == isControl
&& other.isMeta == isMeta
&& other.isAlt == isAlt
&& other.isKeypad == isKeypad;
}
QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) {
@ -188,6 +190,24 @@ void keyEventFromScriptValue(const QScriptValue& object, KeyEvent& event) {
event.key = Qt::Key_Delete;
} else if (event.text.toUpper() == "BACKSPACE") {
event.key = Qt::Key_Backspace;
} else if (event.text.toUpper() == "SHIFT") {
event.key = Qt::Key_Shift;
} else if (event.text.toUpper() == "ALT") {
event.key = Qt::Key_Alt;
} else if (event.text.toUpper() == "CONTROL") {
event.key = Qt::Key_Control;
} else if (event.text.toUpper() == "META") {
event.key = Qt::Key_Meta;
} else if (event.text.toUpper() == "PAGE DOWN") {
event.key = Qt::Key_PageDown;
} else if (event.text.toUpper() == "PAGE UP") {
event.key = Qt::Key_PageUp;
} else if (event.text.toUpper() == "HOME") {
event.key = Qt::Key_Home;
} else if (event.text.toUpper() == "END") {
event.key = Qt::Key_End;
} else if (event.text.toUpper() == "HELP") {
event.key = Qt::Key_Help;
} else {
event.key = event.text.at(0).unicode();
}
@ -224,10 +244,67 @@ void keyEventFromScriptValue(const QScriptValue& object, KeyEvent& event) {
}
}
MouseEvent::MouseEvent() :
x(0.0f),
y(0.0f),
isLeftButton(false),
isRightButton(false),
isMiddleButton(false),
isShifted(false),
isControl(false),
isMeta(false),
isAlt(false)
{
};
MouseEvent::MouseEvent(const QMouseEvent& event) {
x = event.x();
y = event.y();
// single button that caused the event
switch (event.button()) {
case Qt::LeftButton:
button = "LEFT";
isLeftButton = true;
break;
case Qt::RightButton:
button = "RIGHT";
isRightButton = true;
break;
case Qt::MiddleButton:
button = "MIDDLE";
isMiddleButton = true;
break;
default:
button = "NONE";
break;
}
// button pressed state
isLeftButton = isLeftButton || (event.buttons().testFlag(Qt::LeftButton));
isRightButton = isRightButton || (event.buttons().testFlag(Qt::RightButton));
isMiddleButton = isMiddleButton || (event.buttons().testFlag(Qt::MiddleButton));
// keyboard modifiers
isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
isMeta = event.modifiers().testFlag(Qt::MetaModifier);
isControl = event.modifiers().testFlag(Qt::ControlModifier);
isAlt = event.modifiers().testFlag(Qt::AltModifier);
}
QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", event.x);
obj.setProperty("y", event.y);
obj.setProperty("button", event.button);
obj.setProperty("isLeftButton", event.isLeftButton);
obj.setProperty("isRightButton", event.isRightButton);
obj.setProperty("isMiddleButton", event.isMiddleButton);
obj.setProperty("isShifted", event.isShifted);
obj.setProperty("isMeta", event.isMeta);
obj.setProperty("isControl", event.isControl);
obj.setProperty("isAlt", event.isAlt);
return obj;
}
@ -235,10 +312,192 @@ void mouseEventFromScriptValue(const QScriptValue& object, MouseEvent& event) {
// nothing for now...
}
TouchEvent::TouchEvent() :
x(0.0f),
y(0.0f),
isPressed(false),
isMoved(false),
isStationary(false),
isReleased(false),
isShifted(false),
isControl(false),
isMeta(false),
isAlt(false),
touchPoints(0),
points(),
radius(0.0f),
isPinching(false),
isPinchOpening(false),
angles(),
angle(0.0f),
deltaAngle(0.0f),
isRotating(false),
rotating("none")
{
};
TouchEvent::TouchEvent(const QTouchEvent& event) :
// these values are not set by initWithQTouchEvent() because they only apply to comparing to other events
isPinching(false),
isPinchOpening(false),
deltaAngle(0.0f),
isRotating(false),
rotating("none")
{
initWithQTouchEvent(event);
}
TouchEvent::TouchEvent(const QTouchEvent& event, const TouchEvent& other) {
initWithQTouchEvent(event);
calculateMetaAttributes(other);
}
// returns the degrees between two points (note: 0 degrees is 'east')
float angleBetweenPoints(const glm::vec2& a, const glm::vec2& b ) {
glm::vec2 length = b - a;
float radian = std::atan2(length.y, length.x);
float angle = radian * 180.0f / PIE;
if (angle < 0) {
angle += 360.0f;
};
return angle;
}
void TouchEvent::initWithQTouchEvent(const QTouchEvent& event) {
// convert the touch points into an average
const QList<QTouchEvent::TouchPoint>& tPoints = event.touchPoints();
float touchAvgX = 0.0f;
float touchAvgY = 0.0f;
touchPoints = tPoints.count();
if (touchPoints > 1) {
for (int i = 0; i < touchPoints; ++i) {
touchAvgX += tPoints[i].pos().x();
touchAvgY += tPoints[i].pos().y();
// add it to our points vector
glm::vec2 thisPoint(tPoints[i].pos().x(), tPoints[i].pos().y());
points << thisPoint;
}
touchAvgX /= (float)(touchPoints);
touchAvgY /= (float)(touchPoints);
} else {
// I'm not sure this should ever happen, why would Qt send us a touch event for only one point?
// maybe this happens in the case of a multi-touch where all but the last finger is released?
touchAvgX = tPoints[0].pos().x();
touchAvgY = tPoints[0].pos().y();
}
x = touchAvgX;
y = touchAvgY;
// after calculating the center point (average touch point), determine the maximum radius
// also calculate the rotation angle for each point
float maxRadius = 0.0f;
glm::vec2 center(x,y);
for (int i = 0; i < touchPoints; ++i) {
glm::vec2 touchPoint(tPoints[i].pos().x(), tPoints[i].pos().y());
float thisRadius = glm::distance(center,touchPoint);
if (thisRadius > maxRadius) {
maxRadius = thisRadius;
}
// calculate the angle for this point
float thisAngle = angleBetweenPoints(center,touchPoint);
angles << thisAngle;
}
radius = maxRadius;
// after calculating the angles for each touch point, determine the average angle
float totalAngle = 0.0f;
for (int i = 0; i < touchPoints; ++i) {
totalAngle += angles[i];
}
angle = totalAngle/(float)touchPoints;
isPressed = event.touchPointStates().testFlag(Qt::TouchPointPressed);
isMoved = event.touchPointStates().testFlag(Qt::TouchPointMoved);
isStationary = event.touchPointStates().testFlag(Qt::TouchPointStationary);
isReleased = event.touchPointStates().testFlag(Qt::TouchPointReleased);
// keyboard modifiers
isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
isMeta = event.modifiers().testFlag(Qt::MetaModifier);
isControl = event.modifiers().testFlag(Qt::ControlModifier);
isAlt = event.modifiers().testFlag(Qt::AltModifier);
}
void TouchEvent::calculateMetaAttributes(const TouchEvent& other) {
// calculate comparative event attributes...
if (other.radius > radius) {
isPinching = true;
isPinchOpening = false;
} else if (other.radius < radius) {
isPinchOpening = true;
isPinching = false;
} else {
isPinching = other.isPinching;
isPinchOpening = other.isPinchOpening;
}
// determine if the points are rotating...
// note: if the number of touch points change between events, then we don't consider ourselves to be rotating
if (touchPoints == other.touchPoints) {
deltaAngle = angle - other.angle;
if (other.angle < angle) {
isRotating = true;
rotating = "clockwise";
} else if (other.angle > angle) {
isRotating = true;
rotating = "counterClockwise";
} else {
isRotating = false;
rotating = "none";
}
} else {
deltaAngle = 0.0f;
isRotating = false;
rotating = "none";
}
}
QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", event.x);
obj.setProperty("y", event.y);
obj.setProperty("isPressed", event.isPressed);
obj.setProperty("isMoved", event.isMoved);
obj.setProperty("isStationary", event.isStationary);
obj.setProperty("isReleased", event.isReleased);
obj.setProperty("isShifted", event.isShifted);
obj.setProperty("isMeta", event.isMeta);
obj.setProperty("isControl", event.isControl);
obj.setProperty("isAlt", event.isAlt);
obj.setProperty("touchPoints", event.touchPoints);
QScriptValue pointsObj = engine->newArray();
int index = 0;
foreach (glm::vec2 point, event.points) {
QScriptValue thisPoint = vec2toScriptValue(engine, point);
pointsObj.setProperty(index, thisPoint);
index++;
}
obj.setProperty("points", pointsObj);
obj.setProperty("radius", event.radius);
obj.setProperty("isPinching", event.isPinching);
obj.setProperty("isPinchOpening", event.isPinchOpening);
obj.setProperty("angle", event.angle);
obj.setProperty("deltaAngle", event.deltaAngle);
QScriptValue anglesObj = engine->newArray();
index = 0;
foreach (float angle, event.angles) {
anglesObj.setProperty(index, angle);
index++;
}
obj.setProperty("angles", anglesObj);
obj.setProperty("isRotating", event.isRotating);
obj.setProperty("rotating", event.rotating);
return obj;
}
@ -246,10 +505,58 @@ void touchEventFromScriptValue(const QScriptValue& object, TouchEvent& event) {
// nothing for now...
}
WheelEvent::WheelEvent() :
x(0.0f),
y(0.0f),
delta(0.0f),
orientation("UNKNOwN"),
isLeftButton(false),
isRightButton(false),
isMiddleButton(false),
isShifted(false),
isControl(false),
isMeta(false),
isAlt(false)
{
};
WheelEvent::WheelEvent(const QWheelEvent& event) {
x = event.x();
y = event.y();
delta = event.delta();
if (event.orientation() == Qt::Horizontal) {
orientation = "HORIZONTAL";
} else {
orientation = "VERTICAL";
}
// button pressed state
isLeftButton = (event.buttons().testFlag(Qt::LeftButton));
isRightButton = (event.buttons().testFlag(Qt::RightButton));
isMiddleButton = (event.buttons().testFlag(Qt::MiddleButton));
// keyboard modifiers
isShifted = event.modifiers().testFlag(Qt::ShiftModifier);
isMeta = event.modifiers().testFlag(Qt::MetaModifier);
isControl = event.modifiers().testFlag(Qt::ControlModifier);
isAlt = event.modifiers().testFlag(Qt::AltModifier);
}
QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", event.x);
obj.setProperty("y", event.y);
obj.setProperty("delta", event.delta);
obj.setProperty("orientation", event.orientation);
obj.setProperty("isLeftButton", event.isLeftButton);
obj.setProperty("isRightButton", event.isRightButton);
obj.setProperty("isMiddleButton", event.isMiddleButton);
obj.setProperty("isShifted", event.isShifted);
obj.setProperty("isMeta", event.isMeta);
obj.setProperty("isControl", event.isControl);
obj.setProperty("isAlt", event.isAlt);
return obj;
}

View file

@ -23,13 +23,7 @@ class KeyEvent {
public:
KeyEvent();
KeyEvent(const QKeyEvent& event);
inline bool operator==(const KeyEvent& other) const {
return other.key == key
&& other.isShifted == isShifted
&& other.isControl == isControl
&& other.isMeta == isMeta
&& other.isAlt == isAlt
&& other.isKeypad == isKeypad; }
bool operator==(const KeyEvent& other) const;
int key;
QString text;
bool isShifted;
@ -43,26 +37,67 @@ public:
class MouseEvent {
public:
MouseEvent() : x(0), y(0) { };
MouseEvent();
MouseEvent(const QMouseEvent& event);
int x;
int y;
QString button;
bool isLeftButton;
bool isRightButton;
bool isMiddleButton;
bool isShifted;
bool isControl;
bool isMeta;
bool isAlt;
};
class TouchEvent {
public:
TouchEvent() : x(0), y(0) { };
TouchEvent();
TouchEvent(const QTouchEvent& event);
TouchEvent(const QTouchEvent& event, const TouchEvent& other);
float x;
float y;
bool isPressed;
bool isMoved;
bool isStationary;
bool isReleased;
bool isShifted;
bool isControl;
bool isMeta;
bool isAlt;
int touchPoints;
QVector<glm::vec2> points;
float radius;
bool isPinching;
bool isPinchOpening;
QVector<float> angles; // angle from center to each point
float angle; // the average of the angles
float deltaAngle; // the change in average angle from last event
bool isRotating;
QString rotating;
private:
void initWithQTouchEvent(const QTouchEvent& event);
void calculateMetaAttributes(const TouchEvent& other);
};
class WheelEvent {
public:
WheelEvent() : x(0), y(0) { };
WheelEvent();
WheelEvent(const QWheelEvent& event);
int x;
int y;
int delta;
QString orientation;
bool isLeftButton;
bool isRightButton;
bool isMiddleButton;
bool isShifted;
bool isControl;
bool isMeta;
bool isAlt;
};
Q_DECLARE_METATYPE(KeyEvent)

View file

@ -125,6 +125,7 @@ void ScriptEngine::init() {
qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue);
qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);
qScriptRegisterSequenceMetaType<QVector<ParticleID> >(&_engine);
qScriptRegisterSequenceMetaType<QVector<glm::vec2> >(&_engine);
QScriptValue soundConstructorValue = _engine.newFunction(soundConstructor);
QScriptValue soundMetaObject = _engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue);

View file

@ -81,7 +81,6 @@ QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay)
QScriptValue direction = vec3toScriptValue(engine, pickRay.direction);
obj.setProperty("direction", direction);
return obj;
}
void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay) {

View file

@ -46,5 +46,4 @@ Q_DECLARE_METATYPE(PickRay)
QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay);
void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay);
#endif

View file

@ -9,11 +9,12 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm
set(TARGET_NAME voxels)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Script REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME})
qt5_use_modules(${TARGET_NAME} Widgets)
qt5_use_modules(${TARGET_NAME} Widgets Script)
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})

View file

@ -5,10 +5,13 @@
// Created by Brad Hefta-Gaub on 1/29/2014
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <RegisteredMetaTypes.h>
#include "VoxelDetail.h"
void registerVoxelMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, voxelDetailToScriptValue, voxelDetailFromScriptValue);
qScriptRegisterMetaType(engine, rayToVoxelIntersectionResultToScriptValue, rayToVoxelIntersectionResultFromScriptValue);
}
QScriptValue voxelDetailToScriptValue(QScriptEngine* engine, const VoxelDetail& voxelDetail) {
@ -33,5 +36,78 @@ void voxelDetailFromScriptValue(const QScriptValue &object, VoxelDetail& voxelDe
voxelDetail.blue = object.property("blue").toVariant().toInt();
}
RayToVoxelIntersectionResult::RayToVoxelIntersectionResult() :
intersects(false),
voxel(),
distance(0),
face()
{
};
QScriptValue rayToVoxelIntersectionResultToScriptValue(QScriptEngine* engine, const RayToVoxelIntersectionResult& value) {
QScriptValue obj = engine->newObject();
obj.setProperty("intersects", value.intersects);
QScriptValue voxelValue = voxelDetailToScriptValue(engine, value.voxel);
obj.setProperty("voxel", voxelValue);
obj.setProperty("distance", value.distance);
QString faceName = "";
// handle BoxFace
switch (value.face) {
case MIN_X_FACE:
faceName = "MIN_X_FACE";
break;
case MAX_X_FACE:
faceName = "MAX_X_FACE";
break;
case MIN_Y_FACE:
faceName = "MIN_Y_FACE";
break;
case MAX_Y_FACE:
faceName = "MAX_Y_FACE";
break;
case MIN_Z_FACE:
faceName = "MIN_Z_FACE";
break;
case MAX_Z_FACE:
faceName = "MAX_Z_FACE";
break;
}
obj.setProperty("face", faceName);
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
obj.setProperty("intersection", intersection);
return obj;
}
void rayToVoxelIntersectionResultFromScriptValue(const QScriptValue& object, RayToVoxelIntersectionResult& value) {
value.intersects = object.property("intersects").toVariant().toBool();
QScriptValue voxelValue = object.property("voxel");
if (voxelValue.isValid()) {
voxelDetailFromScriptValue(voxelValue, value.voxel);
}
value.distance = object.property("distance").toVariant().toFloat();
QString faceName = object.property("face").toVariant().toString();
if (faceName == "MIN_X_FACE") {
value.face = MIN_X_FACE;
} else if (faceName == "MAX_X_FACE") {
value.face = MAX_X_FACE;
} else if (faceName == "MIN_Y_FACE") {
value.face = MIN_Y_FACE;
} else if (faceName == "MAX_Y_FACE") {
value.face = MAX_Y_FACE;
} else if (faceName == "MIN_Z_FACE") {
value.face = MIN_Z_FACE;
} else {
value.face = MAX_Z_FACE;
};
QScriptValue intersection = object.property("intersection");
if (intersection.isValid()) {
vec3FromScriptValue(intersection, value.intersection);
}
}

View file

@ -12,6 +12,7 @@
#include <QtScript/QScriptEngine>
#include <AABox.h>
#include <SharedUtil.h>
#include "VoxelConstants.h"
@ -32,5 +33,19 @@ void registerVoxelMetaTypes(QScriptEngine* engine);
QScriptValue voxelDetailToScriptValue(QScriptEngine* engine, const VoxelDetail& color);
void voxelDetailFromScriptValue(const QScriptValue &object, VoxelDetail& color);
class RayToVoxelIntersectionResult {
public:
RayToVoxelIntersectionResult();
bool intersects;
VoxelDetail voxel;
float distance;
BoxFace face;
glm::vec3 intersection;
};
Q_DECLARE_METATYPE(RayToVoxelIntersectionResult)
QScriptValue rayToVoxelIntersectionResultToScriptValue(QScriptEngine* engine, const RayToVoxelIntersectionResult& results);
void rayToVoxelIntersectionResultFromScriptValue(const QScriptValue& object, RayToVoxelIntersectionResult& results);
#endif /* defined(__hifi__VoxelDetail__) */

View file

@ -41,3 +41,26 @@ void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale
getVoxelPacketSender()->queueVoxelEditMessages(PacketTypeVoxelErase, 1, &deleteVoxelDetail);
}
RayToVoxelIntersectionResult VoxelsScriptingInterface::findRayIntersection(const PickRay& ray) {
RayToVoxelIntersectionResult result;
if (_tree) {
if (_tree->tryLockForRead()) {
OctreeElement* element;
result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face);
if (result.intersects) {
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
result.voxel.x = voxel->getCorner().x;
result.voxel.y = voxel->getCorner().y;
result.voxel.z = voxel->getCorner().z;
result.voxel.s = voxel->getScale();
result.voxel.red = voxel->getColor()[0];
result.voxel.green = voxel->getColor()[1];
result.voxel.blue = voxel->getColor()[2];
result.intersection = ray.origin + (ray.direction * result.distance);
}
_tree->unlock();
}
}
return result;
}

View file

@ -12,18 +12,22 @@
#include <QtCore/QObject>
#include <OctreeScriptingInterface.h>
#include <RegisteredMetaTypes.h>
#include "VoxelConstants.h"
#include "VoxelEditPacketSender.h"
#include "VoxelTree.h"
/// handles scripting of voxel commands from JS passed to assigned clients
class VoxelsScriptingInterface : public OctreeScriptingInterface {
Q_OBJECT
public:
public:
VoxelsScriptingInterface() : _tree(NULL) {};
VoxelEditPacketSender* getVoxelPacketSender() { return (VoxelEditPacketSender*)getPacketSender(); }
virtual NodeType_t getServerNodeType() const { return NodeType::VoxelServer; }
virtual OctreeEditPacketSender* createPacketSender() { return new VoxelEditPacketSender(); }
void setVoxelTree(VoxelTree* tree) { _tree = tree; }
public slots:
/// queues the creation of a voxel which will be sent by calling process on the PacketSender
@ -53,8 +57,12 @@ public slots:
/// \param scale the scale of the voxel (in meter units)
void eraseVoxel(float x, float y, float z, float scale);
/// If the scripting context has visible voxels, this will determine a ray intersection
RayToVoxelIntersectionResult findRayIntersection(const PickRay& ray);
private:
void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails);
VoxelTree* _tree;
};
#endif /* defined(__hifi__VoxelsScriptingInterface__) */

View file

@ -13,9 +13,11 @@ include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/SetupHifiProject.cmake)
setup_hifi_project(${TARGET_NAME} TRUE)
find_package(Qt5Script REQUIRED)
qt5_use_modules(${TARGET_NAME} Script)
# link in the shared library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})