mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-29 10:44:19 +02:00
Merge remote-tracking branch 'upstream/master' into 19640
This commit is contained in:
commit
f4cb17ba4a
38 changed files with 891 additions and 272 deletions
|
@ -9,6 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
var windowDimensions = Controller.getViewportDimensions();
|
||||||
|
|
||||||
var LASER_WIDTH = 4;
|
var LASER_WIDTH = 4;
|
||||||
var LASER_COLOR = { red: 255, green: 0, blue: 0 };
|
var LASER_COLOR = { red: 255, green: 0, blue: 0 };
|
||||||
var LASER_LENGTH_FACTOR = 1.5;
|
var LASER_LENGTH_FACTOR = 1.5;
|
||||||
|
@ -16,6 +18,40 @@ var LASER_LENGTH_FACTOR = 1.5;
|
||||||
var LEFT = 0;
|
var LEFT = 0;
|
||||||
var RIGHT = 1;
|
var RIGHT = 1;
|
||||||
|
|
||||||
|
|
||||||
|
var SPAWN_DISTANCE = 1;
|
||||||
|
var radiusDefault = 0.10;
|
||||||
|
|
||||||
|
var modelURLs = [
|
||||||
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
||||||
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
|
||||||
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
|
||||||
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/newInvader16x16-large-purple.svo",
|
||||||
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/minotaur/mino_full.fbx",
|
||||||
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Combat_tank_V01.FBX",
|
||||||
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/orc.fbx",
|
||||||
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/slimer.fbx",
|
||||||
|
];
|
||||||
|
|
||||||
|
var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/";
|
||||||
|
var numberOfTools = 1;
|
||||||
|
var toolHeight = 50;
|
||||||
|
var toolWidth = 50;
|
||||||
|
var toolVerticalSpacing = 4;
|
||||||
|
var toolsHeight = toolHeight * numberOfTools + toolVerticalSpacing * (numberOfTools - 1);
|
||||||
|
var toolsX = windowDimensions.x - 8 - toolWidth;
|
||||||
|
var toolsY = (windowDimensions.y - toolsHeight) / 2;
|
||||||
|
|
||||||
|
|
||||||
|
var firstModel = Overlays.addOverlay("image", {
|
||||||
|
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
||||||
|
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
|
||||||
|
imageURL: toolIconUrl + "voxel-tool.svg",
|
||||||
|
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight,
|
||||||
|
visible: true,
|
||||||
|
alpha: 0.9
|
||||||
|
});
|
||||||
|
|
||||||
function controller(wichSide) {
|
function controller(wichSide) {
|
||||||
this.side = wichSide;
|
this.side = wichSide;
|
||||||
this.palm = 2 * wichSide;
|
this.palm = 2 * wichSide;
|
||||||
|
@ -46,7 +82,10 @@ function controller(wichSide) {
|
||||||
this.pressing = false; // is trigger being pressed (is pressed now but wasn't previously)
|
this.pressing = false; // is trigger being pressed (is pressed now but wasn't previously)
|
||||||
|
|
||||||
this.grabbing = false;
|
this.grabbing = false;
|
||||||
this.modelID;
|
this.modelID = { isKnownID: false };
|
||||||
|
this.oldModelRotation;
|
||||||
|
this.oldModelPosition;
|
||||||
|
this.oldModelRadius;
|
||||||
|
|
||||||
this.laser = Overlays.addOverlay("line3d", {
|
this.laser = Overlays.addOverlay("line3d", {
|
||||||
position: this.palmPosition,
|
position: this.palmPosition,
|
||||||
|
@ -85,23 +124,19 @@ function controller(wichSide) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.grab = function (modelID) {
|
this.grab = function (modelID, properties) {
|
||||||
if (!modelID.isKnownID) {
|
|
||||||
var identify = Models.identifyModel(modelID);
|
|
||||||
if (!identify.isKnownID) {
|
|
||||||
print("Unknown ID " + identify.id + "(grab)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
modelID = identify;
|
|
||||||
}
|
|
||||||
print("Grabbing " + modelID.id);
|
print("Grabbing " + modelID.id);
|
||||||
this.grabbing = true;
|
this.grabbing = true;
|
||||||
this.modelID = modelID;
|
this.modelID = modelID;
|
||||||
|
|
||||||
|
this.oldModelPosition = properties.position;
|
||||||
|
this.oldModelRotation = properties.modelRotation;
|
||||||
|
this.oldModelRadius = properties.radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.release = function () {
|
this.release = function () {
|
||||||
this.grabbing = false;
|
this.grabbing = false;
|
||||||
this.modelID = 0;
|
this.modelID.isKnownID = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkTrigger = function () {
|
this.checkTrigger = function () {
|
||||||
|
@ -118,6 +153,34 @@ function controller(wichSide) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.checkModel = function (properties) {
|
||||||
|
// P P - Model
|
||||||
|
// /| A - Palm
|
||||||
|
// / | d B - unit vector toward tip
|
||||||
|
// / | X - base of the perpendicular line
|
||||||
|
// A---X----->B d - distance fom axis
|
||||||
|
// x x - distance from A
|
||||||
|
//
|
||||||
|
// |X-A| = (P-A).B
|
||||||
|
// X == A + ((P-A).B)B
|
||||||
|
// d = |P-X|
|
||||||
|
|
||||||
|
var A = this.palmPosition;
|
||||||
|
var B = this.front;
|
||||||
|
var P = properties.position;
|
||||||
|
|
||||||
|
var x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||||
|
var y = Vec3.dot(Vec3.subtract(P, A), this.up);
|
||||||
|
var z = Vec3.dot(Vec3.subtract(P, A), this.right);
|
||||||
|
var X = Vec3.sum(A, Vec3.multiply(B, x));
|
||||||
|
var d = Vec3.length(Vec3.subtract(P, X));
|
||||||
|
|
||||||
|
if (d < properties.radius && 0 < x && x < LASER_LENGTH_FACTOR) {
|
||||||
|
return { valid: true, x: x, y: y, z: z };
|
||||||
|
}
|
||||||
|
return { valid: false };
|
||||||
|
}
|
||||||
|
|
||||||
this.moveLaser = function () {
|
this.moveLaser = function () {
|
||||||
var endPosition = Vec3.sum(this.palmPosition, Vec3.multiply(this.front, LASER_LENGTH_FACTOR));
|
var endPosition = Vec3.sum(this.palmPosition, Vec3.multiply(this.front, LASER_LENGTH_FACTOR));
|
||||||
|
|
||||||
|
@ -143,44 +206,33 @@ function controller(wichSide) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkModel = function (modelID) {
|
this.moveModel = function () {
|
||||||
if (!modelID.isKnownID) {
|
if (this.grabbing) {
|
||||||
var identify = Models.identifyModel(modelID);
|
var newPosition = Vec3.sum(this.palmPosition,
|
||||||
if (!identify.isKnownID) {
|
Vec3.multiply(this.front, this.x));
|
||||||
print("Unknown ID " + identify.id + "(checkModel)");
|
newPosition = Vec3.sum(newPosition,
|
||||||
return;
|
Vec3.multiply(this.up, this.y));
|
||||||
}
|
newPosition = Vec3.sum(newPosition,
|
||||||
modelID = identify;
|
Vec3.multiply(this.right, this.z));
|
||||||
|
|
||||||
|
var newRotation = Quat.multiply(this.rotation,
|
||||||
|
Quat.inverse(this.oldRotation));
|
||||||
|
newRotation = Quat.multiply(newRotation,
|
||||||
|
this.oldModelRotation);
|
||||||
|
|
||||||
|
Models.editModel(this.modelID, {
|
||||||
|
position: newPosition,
|
||||||
|
modelRotation: newRotation
|
||||||
|
});
|
||||||
|
print("Moving " + this.modelID.id);
|
||||||
|
// Vec3.print("Old Position: ", this.oldModelPosition);
|
||||||
|
// Vec3.print("Sav Position: ", newPosition);
|
||||||
|
Quat.print("Old Rotation: ", this.oldModelRotation);
|
||||||
|
Quat.print("New Rotation: ", newRotation);
|
||||||
|
|
||||||
|
this.oldModelRotation = newRotation;
|
||||||
|
this.oldModelPosition = newPosition;
|
||||||
}
|
}
|
||||||
// P P - Model
|
|
||||||
// /| A - Palm
|
|
||||||
// / | d B - unit vector toward tip
|
|
||||||
// / | X - base of the perpendicular line
|
|
||||||
// A---X----->B d - distance fom axis
|
|
||||||
// x x - distance from A
|
|
||||||
//
|
|
||||||
// |X-A| = (P-A).B
|
|
||||||
// X == A + ((P-A).B)B
|
|
||||||
// d = |P-X|
|
|
||||||
|
|
||||||
var A = this.palmPosition;
|
|
||||||
var B = this.front;
|
|
||||||
var P = Models.getModelProperties(modelID).position;
|
|
||||||
|
|
||||||
this.x = Vec3.dot(Vec3.subtract(P, A), B);
|
|
||||||
this.y = Vec3.dot(Vec3.subtract(P, A), this.up);
|
|
||||||
this.z = Vec3.dot(Vec3.subtract(P, A), this.right);
|
|
||||||
var X = Vec3.sum(A, Vec3.multiply(B, this.x));
|
|
||||||
var d = Vec3.length(Vec3.subtract(P, X));
|
|
||||||
|
|
||||||
// Vec3.print("A: ", A);
|
|
||||||
// Vec3.print("B: ", B);
|
|
||||||
// Vec3.print("Particle pos: ", P);
|
|
||||||
// print("d: " + d + ", x: " + this.x);
|
|
||||||
if (d < Models.getModelProperties(modelID).radius && 0 < this.x && this.x < LASER_LENGTH_FACTOR) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update = function () {
|
this.update = function () {
|
||||||
|
@ -205,25 +257,40 @@ function controller(wichSide) {
|
||||||
|
|
||||||
this.checkTrigger();
|
this.checkTrigger();
|
||||||
|
|
||||||
if (this.pressing) {
|
this.moveLaser();
|
||||||
Vec3.print("Looking at: ", this.palmPosition);
|
|
||||||
var foundModels = Models.findModels(this.palmPosition, LASER_LENGTH_FACTOR);
|
|
||||||
for (var i = 0; i < foundModels.length; i++) {
|
|
||||||
print("Model found ID (" + foundModels[i].id + ")");
|
|
||||||
if (this.checkModel(foundModels[i])) {
|
|
||||||
if (this.grab(foundModels[i])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.pressed && this.grabbing) {
|
if (!this.pressed && this.grabbing) {
|
||||||
// release if trigger not pressed anymore.
|
// release if trigger not pressed anymore.
|
||||||
this.release();
|
this.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.moveLaser();
|
if (this.pressing) {
|
||||||
|
Vec3.print("Looking at: ", this.palmPosition);
|
||||||
|
var foundModels = Models.findModels(this.palmPosition, LASER_LENGTH_FACTOR);
|
||||||
|
for (var i = 0; i < foundModels.length; i++) {
|
||||||
|
|
||||||
|
if (!foundModels[i].isKnownID) {
|
||||||
|
var identify = Models.identifyModel(foundModels[i]);
|
||||||
|
if (!identify.isKnownID) {
|
||||||
|
print("Unknown ID " + identify.id + "(update loop)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foundModels[i] = identify;
|
||||||
|
}
|
||||||
|
|
||||||
|
var properties = Models.getModelProperties(foundModels[i]);
|
||||||
|
print("Checking properties: " + properties.id + " " + properties.isKnownID);
|
||||||
|
|
||||||
|
var check = this.checkModel(properties);
|
||||||
|
if (check.valid) {
|
||||||
|
this.grab(foundModels[i], properties);
|
||||||
|
this.x = check.x;
|
||||||
|
this.y = check.y;
|
||||||
|
this.z = check.z;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cleanup = function () {
|
this.cleanup = function () {
|
||||||
|
@ -238,78 +305,44 @@ var leftController = new controller(LEFT);
|
||||||
var rightController = new controller(RIGHT);
|
var rightController = new controller(RIGHT);
|
||||||
|
|
||||||
function moveModels() {
|
function moveModels() {
|
||||||
if (leftController.grabbing) {
|
if (leftController.grabbing && rightController.grabbing && rightController.modelID.id == leftController.modelID.id) {
|
||||||
if (rightController.grabbing) {
|
print("Both controllers");
|
||||||
var properties = Models.getModelProperties(leftController.modelID);
|
var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x));
|
||||||
|
var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x));
|
||||||
var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x));
|
|
||||||
var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x));
|
|
||||||
|
|
||||||
var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5);
|
|
||||||
var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint));
|
|
||||||
|
|
||||||
|
|
||||||
var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x));
|
|
||||||
var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x));
|
|
||||||
|
|
||||||
var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5);
|
|
||||||
var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint));
|
|
||||||
|
|
||||||
var ratio = length / oldLength;
|
|
||||||
|
|
||||||
var newPosition = Vec3.sum(middle,
|
|
||||||
Vec3.multiply(Vec3.subtract(properties.position, oldMiddle), ratio));
|
|
||||||
Vec3.print("Ratio : " + ratio + " New position: ", newPosition);
|
|
||||||
var rotation = Quat.multiply(leftController.rotation,
|
|
||||||
Quat.inverse(leftController.oldRotation));
|
|
||||||
rotation = Quat.multiply(rotation, properties.modelRotation);
|
|
||||||
|
|
||||||
Models.editModel(leftController.modelID, {
|
|
||||||
position: newPosition,
|
|
||||||
//modelRotation: rotation,
|
|
||||||
radius: properties.radius * ratio
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
var newPosition = Vec3.sum(leftController.palmPosition,
|
|
||||||
Vec3.multiply(leftController.front, leftController.x));
|
|
||||||
newPosition = Vec3.sum(newPosition,
|
|
||||||
Vec3.multiply(leftController.up, leftController.y));
|
|
||||||
newPosition = Vec3.sum(newPosition,
|
|
||||||
Vec3.multiply(leftController.right, leftController.z));
|
|
||||||
|
|
||||||
var rotation = Quat.multiply(leftController.rotation,
|
|
||||||
Quat.inverse(leftController.oldRotation));
|
|
||||||
rotation = Quat.multiply(rotation,
|
|
||||||
Models.getModelProperties(leftController.modelID).modelRotation);
|
|
||||||
|
|
||||||
Models.editModel(leftController.modelID, {
|
|
||||||
position: newPosition,
|
|
||||||
modelRotation: rotation
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (rightController.grabbing) {
|
|
||||||
var newPosition = Vec3.sum(rightController.palmPosition,
|
|
||||||
Vec3.multiply(rightController.front, rightController.x));
|
|
||||||
newPosition = Vec3.sum(newPosition,
|
|
||||||
Vec3.multiply(rightController.up, rightController.y));
|
|
||||||
newPosition = Vec3.sum(newPosition,
|
|
||||||
Vec3.multiply(rightController.right, rightController.z));
|
|
||||||
|
|
||||||
var rotation = Quat.multiply(rightController.rotation,
|
var oldMiddle = Vec3.multiply(Vec3.sum(oldLeftPoint, oldRightPoint), 0.5);
|
||||||
Quat.inverse(rightController.oldRotation));
|
var oldLength = Vec3.length(Vec3.subtract(oldLeftPoint, oldRightPoint));
|
||||||
rotation = Quat.multiply(rotation,
|
|
||||||
Models.getModelProperties(rightController.modelID).modelRotation);
|
|
||||||
|
|
||||||
Models.editModel(rightController.modelID, {
|
|
||||||
|
var leftPoint = Vec3.sum(leftController.palmPosition, Vec3.multiply(leftController.front, leftController.x));
|
||||||
|
var rightPoint = Vec3.sum(rightController.palmPosition, Vec3.multiply(rightController.front, rightController.x));
|
||||||
|
|
||||||
|
var middle = Vec3.multiply(Vec3.sum(leftPoint, rightPoint), 0.5);
|
||||||
|
var length = Vec3.length(Vec3.subtract(leftPoint, rightPoint));
|
||||||
|
|
||||||
|
var ratio = length / oldLength;
|
||||||
|
|
||||||
|
var newPosition = Vec3.sum(middle,
|
||||||
|
Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio));
|
||||||
|
Vec3.print("Ratio : " + ratio + " New position: ", newPosition);
|
||||||
|
var rotation = Quat.multiply(leftController.rotation,
|
||||||
|
Quat.inverse(leftController.oldRotation));
|
||||||
|
rotation = Quat.multiply(rotation, leftController.oldModelRotation);
|
||||||
|
|
||||||
|
Models.editModel(leftController.modelID, {
|
||||||
position: newPosition,
|
position: newPosition,
|
||||||
modelRotation: rotation
|
//modelRotation: rotation,
|
||||||
|
radius: leftController.oldModelRadius * ratio
|
||||||
});
|
});
|
||||||
|
|
||||||
|
leftController.oldModelPosition = newPosition;
|
||||||
|
leftController.oldModelRotation = rotation;
|
||||||
|
leftController.oldModelRadius *= ratio;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
leftController.moveModel();
|
||||||
|
rightController.moveModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkController(deltaTime) {
|
function checkController(deltaTime) {
|
||||||
|
@ -318,6 +351,8 @@ function checkController(deltaTime) {
|
||||||
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
||||||
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
||||||
|
|
||||||
|
moveOverlays();
|
||||||
|
|
||||||
// this is expected for hydras
|
// this is expected for hydras
|
||||||
if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
|
if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
|
||||||
//print("no hydra connected?");
|
//print("no hydra connected?");
|
||||||
|
@ -329,14 +364,48 @@ function checkController(deltaTime) {
|
||||||
moveModels();
|
moveModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function moveOverlays() {
|
||||||
|
windowDimensions = Controller.getViewportDimensions();
|
||||||
|
|
||||||
|
toolsX = windowDimensions.x - 8 - toolWidth;
|
||||||
|
toolsY = (windowDimensions.y - toolsHeight) / 2;
|
||||||
|
|
||||||
|
Overlays.addOverlay(firstModel, {
|
||||||
|
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mousePressEvent(event) {
|
||||||
|
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||||
|
var url;
|
||||||
|
|
||||||
|
if (clickedOverlay == firstModel) {
|
||||||
|
url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]);
|
||||||
|
if (url == null) {
|
||||||
|
return; }
|
||||||
|
} else {
|
||||||
|
print("Didn't click on anything");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE));
|
||||||
|
Models.addModel({ position: position,
|
||||||
|
radius: radiusDefault,
|
||||||
|
modelURL: url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function scriptEnding() {
|
function scriptEnding() {
|
||||||
leftController.cleanup();
|
leftController.cleanup();
|
||||||
rightController.cleanup();
|
rightController.cleanup();
|
||||||
|
|
||||||
|
Overlays.deleteOverlay(firstModel);
|
||||||
}
|
}
|
||||||
Script.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
|
||||||
// register the call back so it fires before each data send
|
// register the call back so it fires before each data send
|
||||||
Script.update.connect(checkController);
|
Script.update.connect(checkController);
|
||||||
|
Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ var radiusMinimum = 0.05;
|
||||||
var radiusMaximum = 0.5;
|
var radiusMaximum = 0.5;
|
||||||
|
|
||||||
var modelURLs = [
|
var modelURLs = [
|
||||||
|
"https://s3-us-west-1.amazonaws.com/highfidelity-public/models/music/EVHFrankenstein.fbx",
|
||||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
||||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
|
||||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
|
||||||
|
@ -51,6 +52,12 @@ var currentModelURL = 1;
|
||||||
var numModels = modelURLs.length;
|
var numModels = modelURLs.length;
|
||||||
|
|
||||||
|
|
||||||
|
function getNewVoxelPosition() {
|
||||||
|
var camera = Camera.getPosition();
|
||||||
|
var forwardVector = Quat.getFront(MyAvatar.orientation);
|
||||||
|
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, 2.0));
|
||||||
|
return newPosition;
|
||||||
|
}
|
||||||
|
|
||||||
function keyPressEvent(event) {
|
function keyPressEvent(event) {
|
||||||
debugPrint("event.text=" + event.text);
|
debugPrint("event.text=" + event.text);
|
||||||
|
@ -63,7 +70,20 @@ function keyPressEvent(event) {
|
||||||
rightRecentlyDeleted = false;
|
rightRecentlyDeleted = false;
|
||||||
rightModelAlreadyInHand = false;
|
rightModelAlreadyInHand = false;
|
||||||
}
|
}
|
||||||
|
} else if (event.text == "m") {
|
||||||
|
var URL = Window.prompt("Model URL", "Enter URL, e.g. http://foo.com/model.fbx");
|
||||||
|
Window.alert("Your response was: " + prompt);
|
||||||
|
var modelPosition = getNewVoxelPosition();
|
||||||
|
var properties = { position: { x: modelPosition.x,
|
||||||
|
y: modelPosition.y,
|
||||||
|
z: modelPosition.z },
|
||||||
|
radius: modelRadius,
|
||||||
|
modelURL: URL
|
||||||
|
};
|
||||||
|
newModel = Models.addModel(properties);
|
||||||
|
|
||||||
} else if (event.text == "DELETE" || event.text == "BACKSPACE") {
|
} else if (event.text == "DELETE" || event.text == "BACKSPACE") {
|
||||||
|
|
||||||
if (leftModelAlreadyInHand) {
|
if (leftModelAlreadyInHand) {
|
||||||
print("want to delete leftHandModel=" + leftHandModel);
|
print("want to delete leftHandModel=" + leftHandModel);
|
||||||
Models.deleteModel(leftHandModel);
|
Models.deleteModel(leftHandModel);
|
||||||
|
|
BIN
interface/resources/sounds/mention/Mentioned A.wav
Normal file
BIN
interface/resources/sounds/mention/Mentioned A.wav
Normal file
Binary file not shown.
BIN
interface/resources/sounds/mention/Mentioned B.wav
Normal file
BIN
interface/resources/sounds/mention/Mentioned B.wav
Normal file
Binary file not shown.
BIN
interface/resources/sounds/mention/Mentioned C.wav
Normal file
BIN
interface/resources/sounds/mention/Mentioned C.wav
Normal file
Binary file not shown.
|
@ -63,11 +63,11 @@
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
#include <OctreeSceneStats.h>
|
#include <OctreeSceneStats.h>
|
||||||
#include <LocalVoxelsList.h>
|
#include <LocalVoxelsList.h>
|
||||||
#include <ModelUploader.h>
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "InterfaceVersion.h"
|
#include "InterfaceVersion.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
#include "ModelUploader.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "devices/OculusManager.h"
|
#include "devices/OculusManager.h"
|
||||||
#include "devices/TV3DManager.h"
|
#include "devices/TV3DManager.h"
|
||||||
|
@ -2843,7 +2843,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
||||||
// save absolute translations
|
// save absolute translations
|
||||||
glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
|
glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
|
||||||
glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation();
|
glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation();
|
||||||
|
|
||||||
// get the eye positions relative to the neck and use them to set the face translation
|
// get the eye positions relative to the neck and use them to set the face translation
|
||||||
glm::vec3 leftEyePosition, rightEyePosition;
|
glm::vec3 leftEyePosition, rightEyePosition;
|
||||||
_myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3());
|
_myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3());
|
||||||
|
@ -2857,11 +2857,22 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
||||||
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
|
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
|
||||||
neckPosition);
|
neckPosition);
|
||||||
|
|
||||||
|
// update the attachments to match
|
||||||
|
QVector<glm::vec3> absoluteAttachmentTranslations;
|
||||||
|
glm::vec3 delta = _myAvatar->getSkeletonModel().getTranslation() - absoluteSkeletonTranslation;
|
||||||
|
foreach (Model* attachment, _myAvatar->getAttachmentModels()) {
|
||||||
|
absoluteAttachmentTranslations.append(attachment->getTranslation());
|
||||||
|
attachment->setTranslation(attachment->getTranslation() + delta);
|
||||||
|
}
|
||||||
|
|
||||||
displaySide(_mirrorCamera, true);
|
displaySide(_mirrorCamera, true);
|
||||||
|
|
||||||
// restore absolute translations
|
// restore absolute translations
|
||||||
_myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
|
_myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
|
||||||
_myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation);
|
_myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation);
|
||||||
|
for (int i = 0; i < absoluteAttachmentTranslations.size(); i++) {
|
||||||
|
_myAvatar->getAttachmentModels().at(i)->setTranslation(absoluteAttachmentTranslations.at(i));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
displaySide(_mirrorCamera, true);
|
displaySide(_mirrorCamera, true);
|
||||||
}
|
}
|
||||||
|
@ -3090,6 +3101,16 @@ void Application::setMenuShortcutsEnabled(bool enabled) {
|
||||||
setShortcutsEnabled(_window->menuBar(), enabled);
|
setShortcutsEnabled(_window->menuBar(), enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::uploadModel(ModelType modelType) {
|
||||||
|
ModelUploader* uploader = new ModelUploader(modelType);
|
||||||
|
QThread* thread = new QThread();
|
||||||
|
thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit()));
|
||||||
|
thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
|
||||||
|
uploader->connect(thread, SIGNAL(started()), SLOT(send()));
|
||||||
|
|
||||||
|
thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
void Application::updateWindowTitle(){
|
void Application::updateWindowTitle(){
|
||||||
|
|
||||||
QString buildVersion = " (build " + applicationVersion() + ")";
|
QString buildVersion = " (build " + applicationVersion() + ")";
|
||||||
|
@ -3417,22 +3438,16 @@ void Application::toggleRunningScriptsWidget() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::uploadFST(bool isHead) {
|
|
||||||
ModelUploader* uploader = new ModelUploader(isHead);
|
|
||||||
QThread* thread = new QThread();
|
|
||||||
thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit()));
|
|
||||||
thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
|
|
||||||
uploader->connect(thread, SIGNAL(started()), SLOT(send()));
|
|
||||||
|
|
||||||
thread->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::uploadHead() {
|
void Application::uploadHead() {
|
||||||
uploadFST(true);
|
uploadModel(HEAD_MODEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::uploadSkeleton() {
|
void Application::uploadSkeleton() {
|
||||||
uploadFST(false);
|
uploadModel(SKELETON_MODEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::uploadAttachment() {
|
||||||
|
uploadModel(ATTACHMENT_MODEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) {
|
ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) {
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
#include "scripting/ControllerScriptingInterface.h"
|
#include "scripting/ControllerScriptingInterface.h"
|
||||||
#include "ui/BandwidthDialog.h"
|
#include "ui/BandwidthDialog.h"
|
||||||
#include "ui/BandwidthMeter.h"
|
#include "ui/BandwidthMeter.h"
|
||||||
|
#include "ui/ModelsBrowser.h"
|
||||||
#include "ui/OctreeStatsDialog.h"
|
#include "ui/OctreeStatsDialog.h"
|
||||||
#include "ui/RearMirrorTools.h"
|
#include "ui/RearMirrorTools.h"
|
||||||
#include "ui/LodToolsDialog.h"
|
#include "ui/LodToolsDialog.h"
|
||||||
|
@ -295,9 +296,9 @@ public slots:
|
||||||
void reloadAllScripts();
|
void reloadAllScripts();
|
||||||
void toggleRunningScriptsWidget();
|
void toggleRunningScriptsWidget();
|
||||||
|
|
||||||
void uploadFST(bool isHead);
|
|
||||||
void uploadHead();
|
void uploadHead();
|
||||||
void uploadSkeleton();
|
void uploadSkeleton();
|
||||||
|
void uploadAttachment();
|
||||||
|
|
||||||
void bumpSettings() { ++_numChangedSettings; }
|
void bumpSettings() { ++_numChangedSettings; }
|
||||||
|
|
||||||
|
@ -375,13 +376,11 @@ private:
|
||||||
|
|
||||||
void setMenuShortcutsEnabled(bool enabled);
|
void setMenuShortcutsEnabled(bool enabled);
|
||||||
|
|
||||||
|
void uploadModel(ModelType modelType);
|
||||||
|
|
||||||
static void attachNewHeadToNode(Node *newNode);
|
static void attachNewHeadToNode(Node *newNode);
|
||||||
static void* networkReceive(void* args); // network receive thread
|
static void* networkReceive(void* args); // network receive thread
|
||||||
|
|
||||||
void findAxisAlignment();
|
|
||||||
|
|
||||||
void displayRearMirrorTools();
|
|
||||||
|
|
||||||
MainWindow* _window;
|
MainWindow* _window;
|
||||||
GLCanvas* _glWidget; // our GLCanvas has a couple extra features
|
GLCanvas* _glWidget; // our GLCanvas has a couple extra features
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "scripting/MenuScriptingInterface.h"
|
#include "scripting/MenuScriptingInterface.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
#include "ui/AttachmentsDialog.h"
|
||||||
#include "ui/InfoView.h"
|
#include "ui/InfoView.h"
|
||||||
#include "ui/MetavoxelEditor.h"
|
#include "ui/MetavoxelEditor.h"
|
||||||
#include "ui/ModelsBrowser.h"
|
#include "ui/ModelsBrowser.h"
|
||||||
|
@ -157,6 +158,8 @@ Menu::Menu() :
|
||||||
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
|
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
|
||||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead()));
|
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead()));
|
||||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, Application::getInstance(), SLOT(uploadSkeleton()));
|
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, Application::getInstance(), SLOT(uploadSkeleton()));
|
||||||
|
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadAttachment, 0,
|
||||||
|
Application::getInstance(), SLOT(uploadAttachment()));
|
||||||
addDisabledActionAndSeparator(fileMenu, "Settings");
|
addDisabledActionAndSeparator(fileMenu, "Settings");
|
||||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings()));
|
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings()));
|
||||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings()));
|
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings()));
|
||||||
|
@ -187,6 +190,8 @@ Menu::Menu() :
|
||||||
SLOT(editPreferences()),
|
SLOT(editPreferences()),
|
||||||
QAction::PreferencesRole);
|
QAction::PreferencesRole);
|
||||||
|
|
||||||
|
addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, this, SLOT(editAttachments()));
|
||||||
|
|
||||||
addDisabledActionAndSeparator(editMenu, "Physics");
|
addDisabledActionAndSeparator(editMenu, "Physics");
|
||||||
QObject* avatar = appInstance->getAvatar();
|
QObject* avatar = appInstance->getAvatar();
|
||||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, true,
|
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, true,
|
||||||
|
@ -210,6 +215,10 @@ Menu::Menu() :
|
||||||
toggleChat();
|
toggleChat();
|
||||||
connect(&xmppClient, SIGNAL(connected()), this, SLOT(toggleChat()));
|
connect(&xmppClient, SIGNAL(connected()), this, SLOT(toggleChat()));
|
||||||
connect(&xmppClient, SIGNAL(disconnected()), this, SLOT(toggleChat()));
|
connect(&xmppClient, SIGNAL(disconnected()), this, SLOT(toggleChat()));
|
||||||
|
|
||||||
|
QDir::setCurrent(Application::resourcesPath());
|
||||||
|
// init chat window to listen chat
|
||||||
|
_chatWindow = new ChatWindow(Application::getInstance()->getWindow());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QMenu* viewMenu = addMenu("View");
|
QMenu* viewMenu = addMenu("View");
|
||||||
|
@ -479,6 +488,7 @@ void Menu::loadSettings(QSettings* settings) {
|
||||||
|
|
||||||
_audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0);
|
_audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0);
|
||||||
_fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES);
|
_fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES);
|
||||||
|
_realWorldFieldOfView = loadSetting(settings, "realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES);
|
||||||
_faceshiftEyeDeflection = loadSetting(settings, "faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION);
|
_faceshiftEyeDeflection = loadSetting(settings, "faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION);
|
||||||
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
|
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
|
||||||
_maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS);
|
_maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS);
|
||||||
|
@ -831,6 +841,15 @@ void Menu::editPreferences() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::editAttachments() {
|
||||||
|
if (!_attachmentsDialog) {
|
||||||
|
_attachmentsDialog = new AttachmentsDialog();
|
||||||
|
_attachmentsDialog->show();
|
||||||
|
} else {
|
||||||
|
_attachmentsDialog->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Menu::goToDomain(const QString newDomain) {
|
void Menu::goToDomain(const QString newDomain) {
|
||||||
if (NodeList::getInstance()->getDomainHandler().getHostname() != newDomain) {
|
if (NodeList::getInstance()->getDomainHandler().getHostname() != newDomain) {
|
||||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||||
|
|
|
@ -64,6 +64,7 @@ struct ViewFrustumOffset {
|
||||||
|
|
||||||
class QSettings;
|
class QSettings;
|
||||||
|
|
||||||
|
class AttachmentsDialog;
|
||||||
class BandwidthDialog;
|
class BandwidthDialog;
|
||||||
class LodToolsDialog;
|
class LodToolsDialog;
|
||||||
class MetavoxelEditor;
|
class MetavoxelEditor;
|
||||||
|
@ -84,6 +85,9 @@ public:
|
||||||
void setAudioJitterBufferSamples(float audioJitterBufferSamples) { _audioJitterBufferSamples = audioJitterBufferSamples; }
|
void setAudioJitterBufferSamples(float audioJitterBufferSamples) { _audioJitterBufferSamples = audioJitterBufferSamples; }
|
||||||
float getFieldOfView() const { return _fieldOfView; }
|
float getFieldOfView() const { return _fieldOfView; }
|
||||||
void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; }
|
void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; }
|
||||||
|
float getRealWorldFieldOfView() const { return _realWorldFieldOfView; }
|
||||||
|
void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; }
|
||||||
|
|
||||||
float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; }
|
float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; }
|
||||||
void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; }
|
void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; }
|
||||||
QString getSnapshotsLocation() const;
|
QString getSnapshotsLocation() const;
|
||||||
|
@ -171,6 +175,7 @@ public slots:
|
||||||
private slots:
|
private slots:
|
||||||
void aboutApp();
|
void aboutApp();
|
||||||
void editPreferences();
|
void editPreferences();
|
||||||
|
void editAttachments();
|
||||||
void goToDomainDialog();
|
void goToDomainDialog();
|
||||||
void goToLocation();
|
void goToLocation();
|
||||||
void nameLocation();
|
void nameLocation();
|
||||||
|
@ -228,6 +233,7 @@ private:
|
||||||
int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback
|
int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback
|
||||||
BandwidthDialog* _bandwidthDialog;
|
BandwidthDialog* _bandwidthDialog;
|
||||||
float _fieldOfView; /// in Degrees, doesn't apply to HMD like Oculus
|
float _fieldOfView; /// in Degrees, doesn't apply to HMD like Oculus
|
||||||
|
float _realWorldFieldOfView; // The actual FOV set by the user's monitor size and view distance
|
||||||
float _faceshiftEyeDeflection;
|
float _faceshiftEyeDeflection;
|
||||||
FrustumDrawMode _frustumDrawMode;
|
FrustumDrawMode _frustumDrawMode;
|
||||||
ViewFrustumOffset _viewFrustumOffset;
|
ViewFrustumOffset _viewFrustumOffset;
|
||||||
|
@ -252,6 +258,7 @@ private:
|
||||||
SimpleMovingAverage _fastFPSAverage;
|
SimpleMovingAverage _fastFPSAverage;
|
||||||
QAction* _loginAction;
|
QAction* _loginAction;
|
||||||
QPointer<PreferencesDialog> _preferencesDialog;
|
QPointer<PreferencesDialog> _preferencesDialog;
|
||||||
|
QPointer<AttachmentsDialog> _attachmentsDialog;
|
||||||
QAction* _chatAction;
|
QAction* _chatAction;
|
||||||
QString _snapshotsLocation;
|
QString _snapshotsLocation;
|
||||||
};
|
};
|
||||||
|
@ -261,6 +268,7 @@ namespace MenuOption {
|
||||||
const QString AlignForearmsWithWrists = "Align Forearms with Wrists";
|
const QString AlignForearmsWithWrists = "Align Forearms with Wrists";
|
||||||
const QString AmbientOcclusion = "Ambient Occlusion";
|
const QString AmbientOcclusion = "Ambient Occlusion";
|
||||||
const QString Atmosphere = "Atmosphere";
|
const QString Atmosphere = "Atmosphere";
|
||||||
|
const QString Attachments = "Attachments...";
|
||||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||||
const QString AudioScope = "Audio Scope";
|
const QString AudioScope = "Audio Scope";
|
||||||
const QString AudioScopePause = "Pause Audio Scope";
|
const QString AudioScopePause = "Pause Audio Scope";
|
||||||
|
@ -362,6 +370,7 @@ namespace MenuOption {
|
||||||
const QString TestPing = "Test Ping";
|
const QString TestPing = "Test Ping";
|
||||||
const QString TransmitterDrive = "Transmitter Drive";
|
const QString TransmitterDrive = "Transmitter Drive";
|
||||||
const QString TurnWithHead = "Turn using Head";
|
const QString TurnWithHead = "Turn using Head";
|
||||||
|
const QString UploadAttachment = "Upload Attachment Model";
|
||||||
const QString UploadHead = "Upload Head Model";
|
const QString UploadHead = "Upload Head Model";
|
||||||
const QString UploadSkeleton = "Upload Skeleton Model";
|
const QString UploadSkeleton = "Upload Skeleton Model";
|
||||||
const QString Visage = "Visage";
|
const QString Visage = "Visage";
|
||||||
|
|
|
@ -59,11 +59,11 @@ static const int MAX_CHECK = 30;
|
||||||
static const int QCOMPRESS_HEADER_POSITION = 0;
|
static const int QCOMPRESS_HEADER_POSITION = 0;
|
||||||
static const int QCOMPRESS_HEADER_SIZE = 4;
|
static const int QCOMPRESS_HEADER_SIZE = 4;
|
||||||
|
|
||||||
ModelUploader::ModelUploader(bool isHead) :
|
ModelUploader::ModelUploader(ModelType modelType) :
|
||||||
_lodCount(-1),
|
_lodCount(-1),
|
||||||
_texturesCount(-1),
|
_texturesCount(-1),
|
||||||
_totalSize(0),
|
_totalSize(0),
|
||||||
_isHead(isHead),
|
_modelType(modelType),
|
||||||
_readyToSend(false),
|
_readyToSend(false),
|
||||||
_dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)),
|
_dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)),
|
||||||
_numberOfChecks(MAX_CHECK)
|
_numberOfChecks(MAX_CHECK)
|
||||||
|
@ -190,7 +190,7 @@ bool ModelUploader::zip() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// open the dialog to configure the rest
|
// open the dialog to configure the rest
|
||||||
ModelPropertiesDialog properties(_isHead, mapping, basePath, geometry);
|
ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry);
|
||||||
if (properties.exec() == QDialog::Rejected) {
|
if (properties.exec() == QDialog::Rejected) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ bool ModelUploader::zip() {
|
||||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\"");
|
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\"");
|
||||||
textPart.setBody(nameField);
|
textPart.setBody(nameField);
|
||||||
_dataMultiPart->append(textPart);
|
_dataMultiPart->append(textPart);
|
||||||
_url = S3_URL + ((_isHead)? "/models/heads/" : "/models/skeletons/") + nameField + ".fst";
|
_url = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField + ".fst";
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::warning(NULL,
|
QMessageBox::warning(NULL,
|
||||||
QString("ModelUploader::zip()"),
|
QString("ModelUploader::zip()"),
|
||||||
|
@ -260,11 +260,7 @@ bool ModelUploader::zip() {
|
||||||
QHttpPart textPart;
|
QHttpPart textPart;
|
||||||
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
|
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
|
||||||
" name=\"model_category\"");
|
" name=\"model_category\"");
|
||||||
if (_isHead) {
|
textPart.setBody(MODEL_TYPE_NAMES[_modelType]);
|
||||||
textPart.setBody("heads");
|
|
||||||
} else {
|
|
||||||
textPart.setBody("skeletons");
|
|
||||||
}
|
|
||||||
_dataMultiPart->append(textPart);
|
_dataMultiPart->append(textPart);
|
||||||
|
|
||||||
_readyToSend = true;
|
_readyToSend = true;
|
||||||
|
@ -428,19 +424,24 @@ void ModelUploader::processCheck() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModelUploader::addTextures(const QString& texdir, const FBXGeometry& geometry) {
|
bool ModelUploader::addTextures(const QString& texdir, const FBXGeometry& geometry) {
|
||||||
|
QSet<QByteArray> added;
|
||||||
foreach (FBXMesh mesh, geometry.meshes) {
|
foreach (FBXMesh mesh, geometry.meshes) {
|
||||||
foreach (FBXMeshPart part, mesh.parts) {
|
foreach (FBXMeshPart part, mesh.parts) {
|
||||||
if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty()) {
|
if (!part.diffuseTexture.filename.isEmpty() && part.diffuseTexture.content.isEmpty() &&
|
||||||
|
!added.contains(part.diffuseTexture.filename)) {
|
||||||
if (!addPart(texdir + "/" + part.diffuseTexture.filename,
|
if (!addPart(texdir + "/" + part.diffuseTexture.filename,
|
||||||
QString("texture%1").arg(++_texturesCount), true)) {
|
QString("texture%1").arg(++_texturesCount), true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
added.insert(part.diffuseTexture.filename);
|
||||||
}
|
}
|
||||||
if (!part.normalTexture.filename.isEmpty() && part.normalTexture.content.isEmpty()) {
|
if (!part.normalTexture.filename.isEmpty() && part.normalTexture.content.isEmpty() &&
|
||||||
|
!added.contains(part.normalTexture.filename)) {
|
||||||
if (!addPart(texdir + "/" + part.normalTexture.filename,
|
if (!addPart(texdir + "/" + part.normalTexture.filename,
|
||||||
QString("texture%1").arg(++_texturesCount), true)) {
|
QString("texture%1").arg(++_texturesCount), true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
added.insert(part.normalTexture.filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -510,9 +511,9 @@ bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelPropertiesDialog::ModelPropertiesDialog(bool isHead, const QVariantHash& originalMapping,
|
ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping,
|
||||||
const QString& basePath, const FBXGeometry& geometry) :
|
const QString& basePath, const FBXGeometry& geometry) :
|
||||||
_isHead(isHead),
|
_modelType(modelType),
|
||||||
_originalMapping(originalMapping),
|
_originalMapping(originalMapping),
|
||||||
_basePath(basePath),
|
_basePath(basePath),
|
||||||
_geometry(geometry) {
|
_geometry(geometry) {
|
||||||
|
@ -531,10 +532,12 @@ ModelPropertiesDialog::ModelPropertiesDialog(bool isHead, const QVariantHash& or
|
||||||
_scale->setMaximum(FLT_MAX);
|
_scale->setMaximum(FLT_MAX);
|
||||||
_scale->setSingleStep(0.01);
|
_scale->setSingleStep(0.01);
|
||||||
|
|
||||||
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
|
if (_modelType != ATTACHMENT_MODEL) {
|
||||||
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
|
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
|
||||||
form->addRow("Neck Joint:", _neckJoint = createJointBox());
|
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
|
||||||
if (!isHead) {
|
form->addRow("Neck Joint:", _neckJoint = createJointBox());
|
||||||
|
}
|
||||||
|
if (_modelType == SKELETON_MODEL) {
|
||||||
form->addRow("Root Joint:", _rootJoint = createJointBox());
|
form->addRow("Root Joint:", _rootJoint = createJointBox());
|
||||||
form->addRow("Lean Joint:", _leanJoint = createJointBox());
|
form->addRow("Lean Joint:", _leanJoint = createJointBox());
|
||||||
form->addRow("Head Joint:", _headJoint = createJointBox());
|
form->addRow("Head Joint:", _headJoint = createJointBox());
|
||||||
|
@ -573,10 +576,12 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
|
||||||
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
|
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
|
||||||
|
|
||||||
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
|
||||||
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
|
if (_modelType != ATTACHMENT_MODEL) {
|
||||||
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
|
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
|
||||||
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
|
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
|
||||||
if (!_isHead) {
|
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
|
||||||
|
}
|
||||||
|
if (_modelType == SKELETON_MODEL) {
|
||||||
insertJointMapping(joints, "jointRoot", _rootJoint->currentText());
|
insertJointMapping(joints, "jointRoot", _rootJoint->currentText());
|
||||||
insertJointMapping(joints, "jointLean", _leanJoint->currentText());
|
insertJointMapping(joints, "jointLean", _leanJoint->currentText());
|
||||||
insertJointMapping(joints, "jointHead", _headJoint->currentText());
|
insertJointMapping(joints, "jointHead", _headJoint->currentText());
|
||||||
|
@ -604,10 +609,12 @@ void ModelPropertiesDialog::reset() {
|
||||||
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
|
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
|
||||||
|
|
||||||
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
|
||||||
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
|
if (_modelType != ATTACHMENT_MODEL) {
|
||||||
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
|
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
|
||||||
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
|
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
|
||||||
if (!_isHead) {
|
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
|
||||||
|
}
|
||||||
|
if (_modelType == SKELETON_MODEL) {
|
||||||
setJointText(_rootJoint, jointHash.value("jointRoot").toString());
|
setJointText(_rootJoint, jointHash.value("jointRoot").toString());
|
||||||
setJointText(_leanJoint, jointHash.value("jointLean").toString());
|
setJointText(_leanJoint, jointHash.value("jointLean").toString());
|
||||||
setJointText(_headJoint, jointHash.value("jointHead").toString());
|
setJointText(_headJoint, jointHash.value("jointHead").toString());
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include <FBXReader.h>
|
#include <FBXReader.h>
|
||||||
|
|
||||||
|
#include "ui/ModelsBrowser.h"
|
||||||
|
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
class QDoubleSpinBox;
|
class QDoubleSpinBox;
|
||||||
class QFileInfo;
|
class QFileInfo;
|
||||||
|
@ -30,7 +32,7 @@ class ModelUploader : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ModelUploader(bool isHead);
|
ModelUploader(ModelType type);
|
||||||
~ModelUploader();
|
~ModelUploader();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -49,7 +51,7 @@ private:
|
||||||
int _lodCount;
|
int _lodCount;
|
||||||
int _texturesCount;
|
int _texturesCount;
|
||||||
int _totalSize;
|
int _totalSize;
|
||||||
bool _isHead;
|
ModelType _modelType;
|
||||||
bool _readyToSend;
|
bool _readyToSend;
|
||||||
|
|
||||||
QHttpMultiPart* _dataMultiPart;
|
QHttpMultiPart* _dataMultiPart;
|
||||||
|
@ -73,7 +75,7 @@ class ModelPropertiesDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ModelPropertiesDialog(bool isHead, const QVariantHash& originalMapping,
|
ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping,
|
||||||
const QString& basePath, const FBXGeometry& geometry);
|
const QString& basePath, const FBXGeometry& geometry);
|
||||||
|
|
||||||
QVariantHash getMapping() const;
|
QVariantHash getMapping() const;
|
||||||
|
@ -87,7 +89,7 @@ private:
|
||||||
QComboBox* createJointBox(bool withNone = true) const;
|
QComboBox* createJointBox(bool withNone = true) const;
|
||||||
void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const;
|
void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const;
|
||||||
|
|
||||||
bool _isHead;
|
ModelType _modelType;
|
||||||
QVariantHash _originalMapping;
|
QVariantHash _originalMapping;
|
||||||
QString _basePath;
|
QString _basePath;
|
||||||
FBXGeometry _geometry;
|
FBXGeometry _geometry;
|
||||||
|
|
|
@ -36,6 +36,7 @@ void XmppClient::xmppConnected() {
|
||||||
_publicChatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM);
|
_publicChatRoom = _xmppMUCManager.addRoom(DEFAULT_CHAT_ROOM);
|
||||||
_publicChatRoom->setNickName(AccountManager::getInstance().getAccountInfo().getUsername());
|
_publicChatRoom->setNickName(AccountManager::getInstance().getAccountInfo().getUsername());
|
||||||
_publicChatRoom->join();
|
_publicChatRoom->join();
|
||||||
|
emit joinedPublicChatRoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
void XmppClient::xmppError(QXmppClient::Error error) {
|
void XmppClient::xmppError(QXmppClient::Error error) {
|
||||||
|
|
|
@ -28,6 +28,9 @@ public:
|
||||||
QXmppClient& getXMPPClient() { return _xmppClient; }
|
QXmppClient& getXMPPClient() { return _xmppClient; }
|
||||||
const QXmppMucRoom* getPublicChatRoom() const { return _publicChatRoom; }
|
const QXmppMucRoom* getPublicChatRoom() const { return _publicChatRoom; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void joinedPublicChatRoom();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void xmppConnected();
|
void xmppConnected();
|
||||||
void xmppError(QXmppClient::Error error);
|
void xmppError(QXmppClient::Error error);
|
||||||
|
|
|
@ -126,6 +126,7 @@ void Avatar::simulate(float deltaTime) {
|
||||||
_skeletonModel.simulate(deltaTime);
|
_skeletonModel.simulate(deltaTime);
|
||||||
}
|
}
|
||||||
_skeletonModel.simulate(deltaTime, _hasNewJointRotations);
|
_skeletonModel.simulate(deltaTime, _hasNewJointRotations);
|
||||||
|
simulateAttachments(deltaTime);
|
||||||
_hasNewJointRotations = false;
|
_hasNewJointRotations = false;
|
||||||
|
|
||||||
glm::vec3 headPosition = _position;
|
glm::vec3 headPosition = _position;
|
||||||
|
@ -232,6 +233,17 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
||||||
_skeletonModel.renderBoundingCollisionShapes(0.7f);
|
_skeletonModel.renderBoundingCollisionShapes(0.7f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If this is the avatar being looked at, render a little ball above their head
|
||||||
|
if (_isLookAtTarget) {
|
||||||
|
const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
|
||||||
|
const float LOOK_AT_INDICATOR_HEIGHT = 0.60f;
|
||||||
|
const float LOOK_AT_INDICATOR_COLOR[] = { 0.8f, 0.0f, 0.0f, 0.5f };
|
||||||
|
glPushMatrix();
|
||||||
|
glColor4fv(LOOK_AT_INDICATOR_COLOR);
|
||||||
|
glTranslatef(_position.x, _position.y + (getSkeletonHeight() * LOOK_AT_INDICATOR_HEIGHT), _position.z);
|
||||||
|
glutSolidSphere(LOOK_AT_INDICATOR_RADIUS, 15, 15);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
// quick check before falling into the code below:
|
// quick check before falling into the code below:
|
||||||
// (a 10 degree breadth of an almost 2 meter avatar kicks in at about 12m)
|
// (a 10 degree breadth of an almost 2 meter avatar kicks in at about 12m)
|
||||||
|
@ -338,6 +350,7 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_skeletonModel.render(1.0f, modelRenderMode);
|
_skeletonModel.render(1.0f, modelRenderMode);
|
||||||
|
renderAttachments(modelRenderMode);
|
||||||
getHand()->render(false);
|
getHand()->render(false);
|
||||||
}
|
}
|
||||||
getHead()->render(1.0f, modelRenderMode);
|
getHead()->render(1.0f, modelRenderMode);
|
||||||
|
@ -347,6 +360,29 @@ bool Avatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode render
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Avatar::simulateAttachments(float deltaTime) {
|
||||||
|
for (int i = 0; i < _attachmentModels.size(); i++) {
|
||||||
|
const AttachmentData& attachment = _attachmentData.at(i);
|
||||||
|
Model* model = _attachmentModels.at(i);
|
||||||
|
int jointIndex = getJointIndex(attachment.jointName);
|
||||||
|
glm::vec3 jointPosition;
|
||||||
|
glm::quat jointRotation;
|
||||||
|
if (_skeletonModel.getJointPosition(jointIndex, jointPosition) &&
|
||||||
|
_skeletonModel.getJointRotation(jointIndex, jointRotation)) {
|
||||||
|
model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale);
|
||||||
|
model->setRotation(jointRotation * attachment.rotation);
|
||||||
|
model->setScale(_skeletonModel.getScale() * attachment.scale);
|
||||||
|
model->simulate(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatar::renderAttachments(Model::RenderMode renderMode) {
|
||||||
|
foreach (Model* model, _attachmentModels) {
|
||||||
|
model->render(1.0f, renderMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Avatar::updateJointMappings() {
|
void Avatar::updateJointMappings() {
|
||||||
// no-op; joint mappings come from skeleton model
|
// no-op; joint mappings come from skeleton model
|
||||||
}
|
}
|
||||||
|
@ -667,6 +703,25 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||||
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
|
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||||
|
AvatarData::setAttachmentData(attachmentData);
|
||||||
|
|
||||||
|
// make sure we have as many models as attachments
|
||||||
|
while (_attachmentModels.size() < attachmentData.size()) {
|
||||||
|
Model* model = new Model(this);
|
||||||
|
model->init();
|
||||||
|
_attachmentModels.append(model);
|
||||||
|
}
|
||||||
|
while (_attachmentModels.size() > attachmentData.size()) {
|
||||||
|
delete _attachmentModels.takeLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the urls
|
||||||
|
for (int i = 0; i < attachmentData.size(); i++) {
|
||||||
|
_attachmentModels[i]->setURL(attachmentData.at(i).modelURL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Avatar::setDisplayName(const QString& displayName) {
|
void Avatar::setDisplayName(const QString& displayName) {
|
||||||
AvatarData::setDisplayName(displayName);
|
AvatarData::setDisplayName(displayName);
|
||||||
_displayNameBoundingRect = textRenderer(DISPLAYNAME)->metrics().tightBoundingRect(displayName);
|
_displayNameBoundingRect = textRenderer(DISPLAYNAME)->metrics().tightBoundingRect(displayName);
|
||||||
|
|
|
@ -79,10 +79,11 @@ public:
|
||||||
//setters
|
//setters
|
||||||
void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
|
void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
|
||||||
void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction);
|
void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction);
|
||||||
|
void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; }
|
||||||
//getters
|
//getters
|
||||||
bool isInitialized() const { return _initialized; }
|
bool isInitialized() const { return _initialized; }
|
||||||
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
|
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
|
||||||
|
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
|
||||||
glm::vec3 getChestPosition() const;
|
glm::vec3 getChestPosition() const;
|
||||||
float getScale() const { return _scale; }
|
float getScale() const { return _scale; }
|
||||||
const glm::vec3& getVelocity() const { return _velocity; }
|
const glm::vec3& getVelocity() const { return _velocity; }
|
||||||
|
@ -131,6 +132,7 @@ public:
|
||||||
|
|
||||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||||
|
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||||
virtual void setDisplayName(const QString& displayName);
|
virtual void setDisplayName(const QString& displayName);
|
||||||
virtual void setBillboard(const QByteArray& billboard);
|
virtual void setBillboard(const QByteArray& billboard);
|
||||||
|
|
||||||
|
@ -160,6 +162,7 @@ signals:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SkeletonModel _skeletonModel;
|
SkeletonModel _skeletonModel;
|
||||||
|
QVector<Model*> _attachmentModels;
|
||||||
float _bodyYawDelta;
|
float _bodyYawDelta;
|
||||||
glm::vec3 _velocity;
|
glm::vec3 _velocity;
|
||||||
float _leanScale;
|
float _leanScale;
|
||||||
|
@ -188,6 +191,9 @@ protected:
|
||||||
virtual void renderBody(RenderMode renderMode, float glowLevel = 0.0f);
|
virtual void renderBody(RenderMode renderMode, float glowLevel = 0.0f);
|
||||||
virtual bool shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const;
|
virtual bool shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const;
|
||||||
|
|
||||||
|
void simulateAttachments(float deltaTime);
|
||||||
|
void renderAttachments(Model::RenderMode renderMode);
|
||||||
|
|
||||||
virtual void updateJointMappings();
|
virtual void updateJointMappings();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -195,6 +201,7 @@ private:
|
||||||
bool _initialized;
|
bool _initialized;
|
||||||
QScopedPointer<Texture> _billboardTexture;
|
QScopedPointer<Texture> _billboardTexture;
|
||||||
bool _shouldRenderBillboard;
|
bool _shouldRenderBillboard;
|
||||||
|
bool _isLookAtTarget;
|
||||||
|
|
||||||
void renderBillboard();
|
void renderBillboard();
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,7 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
getHand()->simulate(deltaTime, true);
|
getHand()->simulate(deltaTime, true);
|
||||||
|
|
||||||
_skeletonModel.simulate(deltaTime);
|
_skeletonModel.simulate(deltaTime);
|
||||||
|
simulateAttachments(deltaTime);
|
||||||
|
|
||||||
// copy out the skeleton joints from the model
|
// copy out the skeleton joints from the model
|
||||||
_jointData.resize(_skeletonModel.getJointStateCount());
|
_jointData.resize(_skeletonModel.getJointStateCount());
|
||||||
|
@ -248,14 +249,17 @@ void MyAvatar::updateFromGyros(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the rotation of the avatar's head (as seen by others, not affecting view frustum)
|
// Set the rotation of the avatar's head (as seen by others, not affecting view frustum)
|
||||||
// to be scaled. Pitch is greater to emphasize nodding behavior / synchrony.
|
// to be scaled such that when the user's physical head is pointing at edge of screen, the
|
||||||
const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f;
|
// avatar head is at the edge of the in-world view frustum. So while a real person may move
|
||||||
const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f;
|
// their head only 30 degrees or so, this may correspond to a 90 degree field of view.
|
||||||
const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f;
|
// Note that roll is magnified by a constant because it is not related to field of view.
|
||||||
|
|
||||||
|
float magnifyFieldOfView = Menu::getInstance()->getFieldOfView() / Menu::getInstance()->getRealWorldFieldOfView();
|
||||||
|
|
||||||
Head* head = getHead();
|
Head* head = getHead();
|
||||||
head->setDeltaPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
head->setDeltaPitch(estimatedRotation.x * magnifyFieldOfView);
|
||||||
head->setDeltaYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
head->setDeltaYaw(estimatedRotation.y * magnifyFieldOfView);
|
||||||
head->setDeltaRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
head->setDeltaRoll(estimatedRotation.z);
|
||||||
|
|
||||||
// Update torso lean distance based on accelerometer data
|
// Update torso lean distance based on accelerometer data
|
||||||
const float TORSO_LENGTH = 0.5f;
|
const float TORSO_LENGTH = 0.5f;
|
||||||
|
@ -338,18 +342,15 @@ void MyAvatar::renderHeadMouse(int screenWidth, int screenHeight) const {
|
||||||
|
|
||||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||||
|
|
||||||
|
float pixelsPerDegree = screenHeight / Menu::getInstance()->getFieldOfView();
|
||||||
|
|
||||||
// Display small target box at center or head mouse target that can also be used to measure LOD
|
// Display small target box at center or head mouse target that can also be used to measure LOD
|
||||||
float headPitch = getHead()->getFinalPitch();
|
float headPitch = getHead()->getFinalPitch();
|
||||||
float headYaw = getHead()->getFinalYaw();
|
float headYaw = getHead()->getFinalYaw();
|
||||||
//
|
|
||||||
// It should be noted that the following constant is a function
|
|
||||||
// how far the viewer's head is away from both the screen and the size of the screen,
|
|
||||||
// which are both things we cannot know without adding a calibration phase.
|
|
||||||
//
|
|
||||||
const float PIXELS_PER_VERTICAL_DEGREE = 20.0f;
|
|
||||||
float aspectRatio = (float) screenWidth / (float) screenHeight;
|
float aspectRatio = (float) screenWidth / (float) screenHeight;
|
||||||
int headMouseX = screenWidth / 2.f - headYaw * aspectRatio * PIXELS_PER_VERTICAL_DEGREE;
|
int headMouseX = screenWidth / 2.f - headYaw * aspectRatio * pixelsPerDegree;
|
||||||
int headMouseY = screenHeight / 2.f - headPitch * PIXELS_PER_VERTICAL_DEGREE;
|
int headMouseY = screenHeight / 2.f - headPitch * pixelsPerDegree;
|
||||||
|
|
||||||
glColor3f(1.0f, 1.0f, 1.0f);
|
glColor3f(1.0f, 1.0f, 1.0f);
|
||||||
glDisable(GL_LINE_SMOOTH);
|
glDisable(GL_LINE_SMOOTH);
|
||||||
|
@ -366,8 +367,8 @@ void MyAvatar::renderHeadMouse(int screenWidth, int screenHeight) const {
|
||||||
|
|
||||||
float avgEyePitch = faceshift->getEstimatedEyePitch();
|
float avgEyePitch = faceshift->getEstimatedEyePitch();
|
||||||
float avgEyeYaw = faceshift->getEstimatedEyeYaw();
|
float avgEyeYaw = faceshift->getEstimatedEyeYaw();
|
||||||
int eyeTargetX = (screenWidth / 2) - avgEyeYaw * aspectRatio * PIXELS_PER_VERTICAL_DEGREE;
|
int eyeTargetX = (screenWidth / 2) - avgEyeYaw * aspectRatio * pixelsPerDegree;
|
||||||
int eyeTargetY = (screenHeight / 2) - avgEyePitch * PIXELS_PER_VERTICAL_DEGREE;
|
int eyeTargetY = (screenHeight / 2) - avgEyePitch * pixelsPerDegree;
|
||||||
|
|
||||||
glColor3f(0.0f, 1.0f, 1.0f);
|
glColor3f(0.0f, 1.0f, 1.0f);
|
||||||
glDisable(GL_LINE_SMOOTH);
|
glDisable(GL_LINE_SMOOTH);
|
||||||
|
@ -422,6 +423,24 @@ void MyAvatar::saveData(QSettings* settings) {
|
||||||
|
|
||||||
settings->setValue("faceModelURL", _faceModelURL);
|
settings->setValue("faceModelURL", _faceModelURL);
|
||||||
settings->setValue("skeletonModelURL", _skeletonModelURL);
|
settings->setValue("skeletonModelURL", _skeletonModelURL);
|
||||||
|
|
||||||
|
settings->beginWriteArray("attachmentData");
|
||||||
|
for (int i = 0; i < _attachmentData.size(); i++) {
|
||||||
|
settings->setArrayIndex(i);
|
||||||
|
const AttachmentData& attachment = _attachmentData.at(i);
|
||||||
|
settings->setValue("modelURL", attachment.modelURL);
|
||||||
|
settings->setValue("jointName", attachment.jointName);
|
||||||
|
settings->setValue("translation_x", attachment.translation.x);
|
||||||
|
settings->setValue("translation_y", attachment.translation.y);
|
||||||
|
settings->setValue("translation_z", attachment.translation.z);
|
||||||
|
glm::vec3 eulers = safeEulerAngles(attachment.rotation);
|
||||||
|
settings->setValue("rotation_x", eulers.x);
|
||||||
|
settings->setValue("rotation_y", eulers.y);
|
||||||
|
settings->setValue("rotation_z", eulers.z);
|
||||||
|
settings->setValue("scale", attachment.scale);
|
||||||
|
}
|
||||||
|
settings->endArray();
|
||||||
|
|
||||||
settings->setValue("displayName", _displayName);
|
settings->setValue("displayName", _displayName);
|
||||||
|
|
||||||
settings->endGroup();
|
settings->endGroup();
|
||||||
|
@ -450,6 +469,28 @@ void MyAvatar::loadData(QSettings* settings) {
|
||||||
|
|
||||||
setFaceModelURL(settings->value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl());
|
setFaceModelURL(settings->value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl());
|
||||||
setSkeletonModelURL(settings->value("skeletonModelURL").toUrl());
|
setSkeletonModelURL(settings->value("skeletonModelURL").toUrl());
|
||||||
|
|
||||||
|
QVector<AttachmentData> attachmentData;
|
||||||
|
int attachmentCount = settings->beginReadArray("attachmentData");
|
||||||
|
for (int i = 0; i < attachmentCount; i++) {
|
||||||
|
settings->setArrayIndex(i);
|
||||||
|
AttachmentData attachment;
|
||||||
|
attachment.modelURL = settings->value("modelURL").toUrl();
|
||||||
|
attachment.jointName = settings->value("jointName").toString();
|
||||||
|
attachment.translation.x = loadSetting(settings, "translation_x", 0.0f);
|
||||||
|
attachment.translation.y = loadSetting(settings, "translation_y", 0.0f);
|
||||||
|
attachment.translation.z = loadSetting(settings, "translation_z", 0.0f);
|
||||||
|
glm::vec3 eulers;
|
||||||
|
eulers.x = loadSetting(settings, "rotation_x", 0.0f);
|
||||||
|
eulers.y = loadSetting(settings, "rotation_y", 0.0f);
|
||||||
|
eulers.z = loadSetting(settings, "rotation_z", 0.0f);
|
||||||
|
attachment.rotation = glm::quat(eulers);
|
||||||
|
attachment.scale = loadSetting(settings, "scale", 1.0f);
|
||||||
|
attachmentData.append(attachment);
|
||||||
|
}
|
||||||
|
settings->endArray();
|
||||||
|
setAttachmentData(attachmentData);
|
||||||
|
|
||||||
setDisplayName(settings->value("displayName").toString());
|
setDisplayName(settings->value("displayName").toString());
|
||||||
|
|
||||||
settings->endGroup();
|
settings->endGroup();
|
||||||
|
@ -468,23 +509,6 @@ void MyAvatar::sendKillAvatar() {
|
||||||
NodeList::getInstance()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer);
|
NodeList::getInstance()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
|
|
||||||
// first orbit horizontally
|
|
||||||
glm::quat orientation = getOrientation();
|
|
||||||
const float ANGULAR_SCALE = 0.5f;
|
|
||||||
glm::quat rotation = glm::angleAxis(glm::radians(- deltaX * ANGULAR_SCALE), orientation * IDENTITY_UP);
|
|
||||||
setPosition(position + rotation * (getPosition() - position));
|
|
||||||
orientation = rotation * orientation;
|
|
||||||
setOrientation(orientation);
|
|
||||||
|
|
||||||
// then vertically
|
|
||||||
float oldPitch = getHead()->getBasePitch();
|
|
||||||
getHead()->setBasePitch(oldPitch - deltaY * ANGULAR_SCALE);
|
|
||||||
rotation = glm::angleAxis(glm::radians((getHead()->getBasePitch() - oldPitch)), orientation * IDENTITY_RIGHT);
|
|
||||||
|
|
||||||
setPosition(position + rotation * (getPosition() - position));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MyAvatar::updateLookAtTargetAvatar() {
|
void MyAvatar::updateLookAtTargetAvatar() {
|
||||||
//
|
//
|
||||||
// Look at the avatar whose eyes are closest to the ray in direction of my avatar's head
|
// Look at the avatar whose eyes are closest to the ray in direction of my avatar's head
|
||||||
|
@ -495,6 +519,7 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
||||||
float smallestAngleTo = MIN_LOOKAT_ANGLE;
|
float smallestAngleTo = MIN_LOOKAT_ANGLE;
|
||||||
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
||||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||||
|
avatar->setIsLookAtTarget(false);
|
||||||
if (!avatar->isMyAvatar()) {
|
if (!avatar->isMyAvatar()) {
|
||||||
float angleTo = glm::angle(getHead()->getFinalOrientation() * glm::vec3(0.0f, 0.0f, -1.0f),
|
float angleTo = glm::angle(getHead()->getFinalOrientation() * glm::vec3(0.0f, 0.0f, -1.0f),
|
||||||
glm::normalize(avatar->getHead()->getEyePosition() - getHead()->getEyePosition()));
|
glm::normalize(avatar->getHead()->getEyePosition() - getHead()->getEyePosition()));
|
||||||
|
@ -505,6 +530,9 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_lookAtTargetAvatar) {
|
||||||
|
static_cast<Avatar*>(_lookAtTargetAvatar.data())->setIsLookAtTarget(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::clearLookAtTargetAvatar() {
|
void MyAvatar::clearLookAtTargetAvatar() {
|
||||||
|
@ -548,7 +576,8 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) {
|
||||||
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ?
|
||||||
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
|
||||||
_skeletonModel.render(1.0f, modelRenderMode);
|
_skeletonModel.render(1.0f, modelRenderMode);
|
||||||
|
renderAttachments(modelRenderMode);
|
||||||
|
|
||||||
// Render head so long as the camera isn't inside it
|
// Render head so long as the camera isn't inside it
|
||||||
if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) {
|
if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) {
|
||||||
getHead()->render(1.0f, modelRenderMode);
|
getHead()->render(1.0f, modelRenderMode);
|
||||||
|
@ -732,7 +761,7 @@ void MyAvatar::applyMotor(float deltaTime) {
|
||||||
glm::vec3 targetVelocity = _motorVelocity;
|
glm::vec3 targetVelocity = _motorVelocity;
|
||||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
|
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
|
||||||
// rotate _motorVelocity into world frame
|
// rotate _motorVelocity into world frame
|
||||||
glm::quat rotation = getOrientation();
|
glm::quat rotation = getHead()->getCameraOrientation();
|
||||||
targetVelocity = rotation * _motorVelocity;
|
targetVelocity = rotation * _motorVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,6 @@ public:
|
||||||
|
|
||||||
static void sendKillAvatar();
|
static void sendKillAvatar();
|
||||||
|
|
||||||
void orbit(const glm::vec3& position, int deltaX, int deltaY);
|
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
|
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
|
||||||
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
||||||
void updateLookAtTargetAvatar();
|
void updateLookAtTargetAvatar();
|
||||||
|
|
|
@ -220,15 +220,10 @@ void FaceplusReader::update() {
|
||||||
if (!_referenceInitialized) {
|
if (!_referenceInitialized) {
|
||||||
_referenceX = x;
|
_referenceX = x;
|
||||||
_referenceY = y;
|
_referenceY = y;
|
||||||
_referenceScale = scale;
|
|
||||||
_referenceInitialized = true;
|
_referenceInitialized = true;
|
||||||
}
|
}
|
||||||
const float TRANSLATION_SCALE = 10.0f;
|
const float TRANSLATION_SCALE = 10.0f;
|
||||||
const float REFERENCE_DISTANCE = 10.0f;
|
glm::vec3 headTranslation((x - _referenceX) * TRANSLATION_SCALE, (y - _referenceY) * TRANSLATION_SCALE, 0.0f);
|
||||||
float depthScale = _referenceScale / scale;
|
|
||||||
float z = REFERENCE_DISTANCE * (depthScale - 1.0f);
|
|
||||||
glm::vec3 headTranslation((x - _referenceX) * depthScale * TRANSLATION_SCALE,
|
|
||||||
(y - _referenceY) * depthScale * TRANSLATION_SCALE, z);
|
|
||||||
glm::quat headRotation(glm::radians(glm::vec3(-_outputVector.at(_headRotationIndices[0]),
|
glm::quat headRotation(glm::radians(glm::vec3(-_outputVector.at(_headRotationIndices[0]),
|
||||||
_outputVector.at(_headRotationIndices[1]), -_outputVector.at(_headRotationIndices[2]))));
|
_outputVector.at(_headRotationIndices[1]), -_outputVector.at(_headRotationIndices[2]))));
|
||||||
float estimatedEyePitch = (_outputVector.at(_leftEyeRotationIndices[0]) +
|
float estimatedEyePitch = (_outputVector.at(_leftEyeRotationIndices[0]) +
|
||||||
|
|
|
@ -76,7 +76,6 @@ private:
|
||||||
int _rightEyeRotationIndices[2];
|
int _rightEyeRotationIndices[2];
|
||||||
float _referenceX;
|
float _referenceX;
|
||||||
float _referenceY;
|
float _referenceY;
|
||||||
float _referenceScale;
|
|
||||||
bool _referenceInitialized;
|
bool _referenceInitialized;
|
||||||
QVector<float> _blendshapeCoefficients;
|
QVector<float> _blendshapeCoefficients;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -509,6 +509,24 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
|
||||||
|
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
position = _translation + extractTranslation(_jointStates[jointIndex].transform);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) const {
|
||||||
|
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rotation = _jointStates[jointIndex].combinedRotation *
|
||||||
|
(fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation :
|
||||||
|
_geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Model::clearShapes() {
|
void Model::clearShapes() {
|
||||||
for (int i = 0; i < _jointShapes.size(); ++i) {
|
for (int i = 0; i < _jointShapes.size(); ++i) {
|
||||||
delete _jointShapes[i];
|
delete _jointShapes[i];
|
||||||
|
@ -949,24 +967,6 @@ void Model::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint
|
||||||
// nothing by default
|
// nothing by default
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
|
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
position = _translation + extractTranslation(_jointStates[jointIndex].transform);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) const {
|
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rotation = _jointStates[jointIndex].combinedRotation *
|
|
||||||
(fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation :
|
|
||||||
_geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation,
|
bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation,
|
||||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment) {
|
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment) {
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||||
|
|
|
@ -180,6 +180,9 @@ public:
|
||||||
/// Returns the extended length from the right hand to its first free ancestor.
|
/// Returns the extended length from the right hand to its first free ancestor.
|
||||||
float getRightArmLength() const;
|
float getRightArmLength() const;
|
||||||
|
|
||||||
|
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||||
|
bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const;
|
||||||
|
|
||||||
void clearShapes();
|
void clearShapes();
|
||||||
void rebuildShapes();
|
void rebuildShapes();
|
||||||
void updateShapePositions();
|
void updateShapePositions();
|
||||||
|
@ -269,9 +272,6 @@ protected:
|
||||||
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||||
virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
virtual void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||||
|
|
||||||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
|
||||||
bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const;
|
|
||||||
|
|
||||||
bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(),
|
bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(),
|
||||||
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
|
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
|
||||||
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f));
|
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f));
|
||||||
|
|
169
interface/src/ui/AttachmentsDialog.cpp
Normal file
169
interface/src/ui/AttachmentsDialog.cpp
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
//
|
||||||
|
// AttachmentsDialog.cpp
|
||||||
|
// interface/src/ui
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 5/4/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QDoubleSpinBox>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
#include "AttachmentsDialog.h"
|
||||||
|
|
||||||
|
AttachmentsDialog::AttachmentsDialog() :
|
||||||
|
QDialog(Application::getInstance()->getWindow()) {
|
||||||
|
|
||||||
|
setWindowTitle("Edit Attachments");
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout();
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
QScrollArea* area = new QScrollArea();
|
||||||
|
layout->addWidget(area);
|
||||||
|
area->setWidgetResizable(true);
|
||||||
|
QWidget* container = new QWidget();
|
||||||
|
container->setLayout(_attachments = new QVBoxLayout());
|
||||||
|
container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
||||||
|
area->setWidget(container);
|
||||||
|
_attachments->addStretch(1);
|
||||||
|
|
||||||
|
foreach (const AttachmentData& data, Application::getInstance()->getAvatar()->getAttachmentData()) {
|
||||||
|
addAttachment(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton* newAttachment = new QPushButton("New Attachment");
|
||||||
|
connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment()));
|
||||||
|
layout->addWidget(newAttachment);
|
||||||
|
|
||||||
|
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||||
|
layout->addWidget(buttons);
|
||||||
|
connect(buttons, SIGNAL(accepted()), SLOT(deleteLater()));
|
||||||
|
_ok = buttons->button(QDialogButtonBox::Ok);
|
||||||
|
|
||||||
|
setMinimumSize(600, 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachmentsDialog::setVisible(bool visible) {
|
||||||
|
QDialog::setVisible(visible);
|
||||||
|
|
||||||
|
// un-default the OK button
|
||||||
|
if (visible) {
|
||||||
|
_ok->setDefault(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachmentsDialog::updateAttachmentData() {
|
||||||
|
QVector<AttachmentData> data;
|
||||||
|
for (int i = 0; i < _attachments->count() - 1; i++) {
|
||||||
|
data.append(static_cast<AttachmentPanel*>(_attachments->itemAt(i)->widget())->getAttachmentData());
|
||||||
|
}
|
||||||
|
Application::getInstance()->getAvatar()->setAttachmentData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachmentsDialog::addAttachment(const AttachmentData& data) {
|
||||||
|
_attachments->insertWidget(_attachments->count() - 1, new AttachmentPanel(this, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QDoubleSpinBox* createTranslationBox(AttachmentsDialog* dialog, float value) {
|
||||||
|
QDoubleSpinBox* box = new QDoubleSpinBox();
|
||||||
|
box->setSingleStep(0.01);
|
||||||
|
box->setMinimum(-FLT_MAX);
|
||||||
|
box->setMaximum(FLT_MAX);
|
||||||
|
box->setValue(value);
|
||||||
|
dialog->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QDoubleSpinBox* createRotationBox(AttachmentsDialog* dialog, float value) {
|
||||||
|
QDoubleSpinBox* box = new QDoubleSpinBox();
|
||||||
|
box->setMinimum(-180.0);
|
||||||
|
box->setMaximum(180.0);
|
||||||
|
box->setWrapping(true);
|
||||||
|
box->setValue(value);
|
||||||
|
dialog->connect(box, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data) {
|
||||||
|
setFrameStyle(QFrame::StyledPanel);
|
||||||
|
|
||||||
|
QFormLayout* layout = new QFormLayout();
|
||||||
|
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
QHBoxLayout* urlBox = new QHBoxLayout();
|
||||||
|
layout->addRow("Model URL:", urlBox);
|
||||||
|
urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1);
|
||||||
|
_modelURL->setText(data.modelURL.toString());
|
||||||
|
dialog->connect(_modelURL, SIGNAL(returnPressed()), SLOT(updateAttachmentData()));
|
||||||
|
QPushButton* chooseURL = new QPushButton("Choose");
|
||||||
|
urlBox->addWidget(chooseURL);
|
||||||
|
connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL()));
|
||||||
|
|
||||||
|
layout->addRow("Joint:", _jointName = new QComboBox());
|
||||||
|
QSharedPointer<NetworkGeometry> geometry = Application::getInstance()->getAvatar()->getSkeletonModel().getGeometry();
|
||||||
|
if (geometry && geometry->isLoaded()) {
|
||||||
|
foreach (const FBXJoint& joint, geometry->getFBXGeometry().joints) {
|
||||||
|
_jointName->addItem(joint.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_jointName->setCurrentText(data.jointName);
|
||||||
|
dialog->connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(updateAttachmentData()));
|
||||||
|
|
||||||
|
QHBoxLayout* translationBox = new QHBoxLayout();
|
||||||
|
translationBox->addWidget(_translationX = createTranslationBox(dialog, data.translation.x));
|
||||||
|
translationBox->addWidget(_translationY = createTranslationBox(dialog, data.translation.y));
|
||||||
|
translationBox->addWidget(_translationZ = createTranslationBox(dialog, data.translation.z));
|
||||||
|
layout->addRow("Translation:", translationBox);
|
||||||
|
|
||||||
|
QHBoxLayout* rotationBox = new QHBoxLayout();
|
||||||
|
glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation));
|
||||||
|
rotationBox->addWidget(_rotationX = createRotationBox(dialog, eulers.x));
|
||||||
|
rotationBox->addWidget(_rotationY = createRotationBox(dialog, eulers.y));
|
||||||
|
rotationBox->addWidget(_rotationZ = createRotationBox(dialog, eulers.z));
|
||||||
|
layout->addRow("Rotation:", rotationBox);
|
||||||
|
|
||||||
|
layout->addRow("Scale:", _scale = new QDoubleSpinBox());
|
||||||
|
_scale->setSingleStep(0.01);
|
||||||
|
_scale->setMaximum(FLT_MAX);
|
||||||
|
_scale->setValue(data.scale);
|
||||||
|
dialog->connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
|
||||||
|
|
||||||
|
QPushButton* remove = new QPushButton("Delete");
|
||||||
|
layout->addRow(remove);
|
||||||
|
connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater()));
|
||||||
|
dialog->connect(remove, SIGNAL(clicked(bool)), SLOT(updateAttachmentData()), Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachmentData AttachmentPanel::getAttachmentData() const {
|
||||||
|
AttachmentData data;
|
||||||
|
data.modelURL = _modelURL->text();
|
||||||
|
data.jointName = _jointName->currentText();
|
||||||
|
data.translation = glm::vec3(_translationX->value(), _translationY->value(), _translationZ->value());
|
||||||
|
data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value())));
|
||||||
|
data.scale = _scale->value();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachmentPanel::chooseModelURL() {
|
||||||
|
ModelsBrowser modelBrowser(ATTACHMENT_MODEL, this);
|
||||||
|
connect(&modelBrowser, SIGNAL(selected(QString)), SLOT(setModelURL(const QString&)));
|
||||||
|
modelBrowser.browse();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachmentPanel::setModelURL(const QString& url) {
|
||||||
|
_modelURL->setText(url);
|
||||||
|
emit _modelURL->returnPressed();
|
||||||
|
}
|
77
interface/src/ui/AttachmentsDialog.h
Normal file
77
interface/src/ui/AttachmentsDialog.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// AttachmentsDialog.h
|
||||||
|
// interface/src/ui
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 5/4/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AttachmentsDialog_h
|
||||||
|
#define hifi_AttachmentsDialog_h
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QFrame>
|
||||||
|
|
||||||
|
#include <AvatarData.h>
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
|
class QDoubleSpinner;
|
||||||
|
class QLineEdit;
|
||||||
|
class QVBoxLayout;
|
||||||
|
|
||||||
|
/// Allows users to edit the avatar attachments.
|
||||||
|
class AttachmentsDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
AttachmentsDialog();
|
||||||
|
|
||||||
|
virtual void setVisible(bool visible);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void updateAttachmentData();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void addAttachment(const AttachmentData& data = AttachmentData());
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QVBoxLayout* _attachments;
|
||||||
|
QPushButton* _ok;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A panel controlling a single attachment.
|
||||||
|
class AttachmentPanel : public QFrame {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData());
|
||||||
|
|
||||||
|
AttachmentData getAttachmentData() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void chooseModelURL();
|
||||||
|
void setModelURL(const QString& url);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QLineEdit* _modelURL;
|
||||||
|
QComboBox* _jointName;
|
||||||
|
QDoubleSpinBox* _translationX;
|
||||||
|
QDoubleSpinBox* _translationY;
|
||||||
|
QDoubleSpinBox* _translationZ;
|
||||||
|
QDoubleSpinBox* _rotationX;
|
||||||
|
QDoubleSpinBox* _rotationY;
|
||||||
|
QDoubleSpinBox* _rotationZ;
|
||||||
|
QDoubleSpinBox* _scale;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AttachmentsDialog_h
|
|
@ -26,17 +26,23 @@
|
||||||
|
|
||||||
#include "ChatWindow.h"
|
#include "ChatWindow.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const int NUM_MESSAGES_TO_TIME_STAMP = 20;
|
const int NUM_MESSAGES_TO_TIME_STAMP = 20;
|
||||||
|
|
||||||
const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)");
|
const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)");
|
||||||
const QRegularExpression regexHifiLinks("([#@]\\S+)");
|
const QRegularExpression regexHifiLinks("([#@]\\S+)");
|
||||||
|
const QString mentionSoundsPath("/sounds/mention/");
|
||||||
|
const QString mentionRegex("@(\\b%1\\b)");
|
||||||
|
|
||||||
ChatWindow::ChatWindow(QWidget* parent) :
|
ChatWindow::ChatWindow(QWidget* parent) :
|
||||||
FramelessDialog(parent, 0, POSITION_RIGHT),
|
FramelessDialog(parent, 0, POSITION_RIGHT),
|
||||||
ui(new Ui::ChatWindow),
|
ui(new Ui::ChatWindow),
|
||||||
numMessagesAfterLastTimeStamp(0),
|
numMessagesAfterLastTimeStamp(0),
|
||||||
_mousePressed(false),
|
_mousePressed(false),
|
||||||
_mouseStartPosition()
|
_mouseStartPosition(),
|
||||||
|
_trayIcon(parent),
|
||||||
|
_effectPlayer()
|
||||||
{
|
{
|
||||||
setAttribute(Qt::WA_DeleteOnClose, false);
|
setAttribute(Qt::WA_DeleteOnClose, false);
|
||||||
|
|
||||||
|
@ -77,16 +83,47 @@ ChatWindow::ChatWindow(QWidget* parent) :
|
||||||
ui->usersWidget->hide();
|
ui->usersWidget->hide();
|
||||||
ui->messagesScrollArea->hide();
|
ui->messagesScrollArea->hide();
|
||||||
ui->messagePlainTextEdit->hide();
|
ui->messagePlainTextEdit->hide();
|
||||||
connect(&xmppClient, SIGNAL(connected()), this, SLOT(connected()));
|
connect(&XmppClient::getInstance(), SIGNAL(joinedPublicChatRoom()), this, SLOT(connected()));
|
||||||
}
|
}
|
||||||
connect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage)));
|
connect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage)));
|
||||||
|
connect(&_trayIcon, SIGNAL(messageClicked()), this, SLOT(notificationClicked()));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QDir mentionSoundsDir(Application::resourcesPath() + mentionSoundsPath);
|
||||||
|
_mentionSounds = mentionSoundsDir.entryList(QDir::Files);
|
||||||
|
_trayIcon.setIcon(QIcon( Application::resourcesPath() + "/images/hifi-logo.svg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWindow::notificationClicked() {
|
||||||
|
if (parentWidget()->isMinimized()) {
|
||||||
|
parentWidget()->showNormal();
|
||||||
|
}
|
||||||
|
if (isHidden()) {
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// find last mention
|
||||||
|
int messageCount = ui->messagesVBoxLayout->count();
|
||||||
|
for (unsigned int i = messageCount; i > 0; i--) {
|
||||||
|
ChatMessageArea* area = (ChatMessageArea*)ui->messagesVBoxLayout->itemAt(i - 1)->widget();
|
||||||
|
QRegularExpression usernameMention(mentionRegex.arg(AccountManager::getInstance().getAccountInfo().getUsername()));
|
||||||
|
if (area->toPlainText().contains(usernameMention)) {
|
||||||
|
int top = area->geometry().top();
|
||||||
|
int height = area->geometry().height();
|
||||||
|
|
||||||
|
QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar();
|
||||||
|
verticalScrollBar->setSliderPosition(top - verticalScrollBar->size().height() + height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatWindow::~ChatWindow() {
|
ChatWindow::~ChatWindow() {
|
||||||
#ifdef HAVE_QXMPP
|
#ifdef HAVE_QXMPP
|
||||||
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
|
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
|
||||||
disconnect(&xmppClient, SIGNAL(connected()), this, SLOT(connected()));
|
disconnect(&xmppClient, SIGNAL(joinedPublicChatRoom()), this, SLOT(connected()));
|
||||||
disconnect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage)));
|
disconnect(&xmppClient, SIGNAL(messageReceived(QXmppMessage)), this, SLOT(messageReceived(QXmppMessage)));
|
||||||
|
|
||||||
const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom();
|
const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom();
|
||||||
|
@ -105,9 +142,15 @@ void ChatWindow::keyPressEvent(QKeyEvent* event) {
|
||||||
|
|
||||||
void ChatWindow::showEvent(QShowEvent* event) {
|
void ChatWindow::showEvent(QShowEvent* event) {
|
||||||
FramelessDialog::showEvent(event);
|
FramelessDialog::showEvent(event);
|
||||||
|
|
||||||
if (!event->spontaneous()) {
|
if (!event->spontaneous()) {
|
||||||
ui->messagePlainTextEdit->setFocus();
|
ui->messagePlainTextEdit->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
|
||||||
|
if (xmppClient.isConnected()) {
|
||||||
|
participantsChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatWindow::eventFilter(QObject* sender, QEvent* event) {
|
bool ChatWindow::eventFilter(QObject* sender, QEvent* event) {
|
||||||
|
@ -304,6 +347,21 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
|
||||||
} else {
|
} else {
|
||||||
lastMessageStamp = QDateTime::currentDateTime();
|
lastMessageStamp = QDateTime::currentDateTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRegularExpression usernameMention(mentionRegex.arg(AccountManager::getInstance().getAccountInfo().getUsername()));
|
||||||
|
if (isHidden() && message.body().contains(usernameMention)) {
|
||||||
|
if (_effectPlayer.state() != QMediaPlayer::PlayingState) {
|
||||||
|
// get random sound
|
||||||
|
QFileInfo inf = QFileInfo(Application::resourcesPath() +
|
||||||
|
mentionSoundsPath +
|
||||||
|
_mentionSounds.at(rand() % _mentionSounds.size()));
|
||||||
|
_effectPlayer.setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||||
|
_effectPlayer.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
_trayIcon.show();
|
||||||
|
_trayIcon.showMessage(windowTitle(), message.body());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
|
#include <QMediaPlayer>
|
||||||
|
#include <QSystemTrayIcon>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include <Application.h>
|
#include <Application.h>
|
||||||
|
@ -63,6 +65,9 @@ private:
|
||||||
QDateTime lastMessageStamp;
|
QDateTime lastMessageStamp;
|
||||||
bool _mousePressed;
|
bool _mousePressed;
|
||||||
QPoint _mouseStartPosition;
|
QPoint _mouseStartPosition;
|
||||||
|
QSystemTrayIcon _trayIcon;
|
||||||
|
QStringList _mentionSounds;
|
||||||
|
QMediaPlayer _effectPlayer;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void connected();
|
void connected();
|
||||||
|
@ -71,6 +76,7 @@ private slots:
|
||||||
void error(QXmppClient::Error error);
|
void error(QXmppClient::Error error);
|
||||||
void participantsChanged();
|
void participantsChanged();
|
||||||
void messageReceived(const QXmppMessage& message);
|
void messageReceived(const QXmppMessage& message);
|
||||||
|
void notificationClicked();
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,11 @@
|
||||||
|
|
||||||
#include "ModelsBrowser.h"
|
#include "ModelsBrowser.h"
|
||||||
|
|
||||||
|
const char* MODEL_TYPE_NAMES[] = { "heads", "skeletons", "attachments" };
|
||||||
|
|
||||||
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
|
||||||
static const QString PUBLIC_URL = "http://public.highfidelity.io";
|
static const QString PUBLIC_URL = "http://public.highfidelity.io";
|
||||||
static const QString HEAD_MODELS_LOCATION = "models/heads";
|
static const QString MODELS_LOCATION = "models/";
|
||||||
static const QString SKELETON_MODELS_LOCATION = "models/skeletons/";
|
|
||||||
|
|
||||||
static const QString PREFIX_PARAMETER_NAME = "prefix";
|
static const QString PREFIX_PARAMETER_NAME = "prefix";
|
||||||
static const QString MARKER_PARAMETER_NAME = "marker";
|
static const QString MARKER_PARAMETER_NAME = "marker";
|
||||||
|
@ -243,11 +244,7 @@ void ModelHandler::queryNewFiles(QString marker) {
|
||||||
// Build query
|
// Build query
|
||||||
QUrl url(S3_URL);
|
QUrl url(S3_URL);
|
||||||
QUrlQuery query;
|
QUrlQuery query;
|
||||||
if (_type == Head) {
|
query.addQueryItem(PREFIX_PARAMETER_NAME, MODELS_LOCATION + MODEL_TYPE_NAMES[_type]);
|
||||||
query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION);
|
|
||||||
} else if (_type == Skeleton) {
|
|
||||||
query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!marker.isEmpty()) {
|
if (!marker.isEmpty()) {
|
||||||
query.addQueryItem(MARKER_PARAMETER_NAME, marker);
|
query.addQueryItem(MARKER_PARAMETER_NAME, marker);
|
||||||
|
|
|
@ -16,12 +16,14 @@
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
|
|
||||||
|
|
||||||
enum ModelType {
|
enum ModelType {
|
||||||
Head,
|
HEAD_MODEL,
|
||||||
Skeleton
|
SKELETON_MODEL,
|
||||||
|
ATTACHMENT_MODEL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const char* MODEL_TYPE_NAMES[];
|
||||||
|
|
||||||
class ModelHandler : public QObject {
|
class ModelHandler : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -48,7 +48,7 @@ void PreferencesDialog::openHeadModelBrowser() {
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
|
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
|
||||||
show();
|
show();
|
||||||
|
|
||||||
ModelsBrowser modelBrowser(Head);
|
ModelsBrowser modelBrowser(HEAD_MODEL);
|
||||||
connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setHeadUrl);
|
connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setHeadUrl);
|
||||||
modelBrowser.browse();
|
modelBrowser.browse();
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ void PreferencesDialog::openBodyModelBrowser() {
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
|
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
|
||||||
show();
|
show();
|
||||||
|
|
||||||
ModelsBrowser modelBrowser(Skeleton);
|
ModelsBrowser modelBrowser(SKELETON_MODEL);
|
||||||
connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl);
|
connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl);
|
||||||
modelBrowser.browse();
|
modelBrowser.browse();
|
||||||
|
|
||||||
|
|
|
@ -198,9 +198,6 @@ color: #0e7077</string>
|
||||||
<property name="indent">
|
<property name="indent">
|
||||||
<number>25</number>
|
<number>25</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
|
||||||
<cstring></cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -510,7 +507,7 @@ color: #0e7077</string>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="headLabel">
|
<widget class="QLabel" name="headLabel_3">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -547,7 +544,7 @@ color: #0e7077</string>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout_1">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="snapshotLocationEdit">
|
<widget class="QLineEdit" name="snapshotLocationEdit">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -564,7 +561,7 @@ color: #0e7077</string>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer_1">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
@ -599,8 +599,9 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) {
|
||||||
|
|
||||||
QUuid avatarUUID;
|
QUuid avatarUUID;
|
||||||
QUrl faceModelURL, skeletonModelURL;
|
QUrl faceModelURL, skeletonModelURL;
|
||||||
|
QVector<AttachmentData> attachmentData;
|
||||||
QString displayName;
|
QString displayName;
|
||||||
packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL >> displayName;
|
packetStream >> avatarUUID >> faceModelURL >> skeletonModelURL >> attachmentData >> displayName;
|
||||||
|
|
||||||
bool hasIdentityChanged = false;
|
bool hasIdentityChanged = false;
|
||||||
|
|
||||||
|
@ -618,7 +619,12 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray &packet) {
|
||||||
setDisplayName(displayName);
|
setDisplayName(displayName);
|
||||||
hasIdentityChanged = true;
|
hasIdentityChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attachmentData != _attachmentData) {
|
||||||
|
setAttachmentData(attachmentData);
|
||||||
|
hasIdentityChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
return hasIdentityChanged;
|
return hasIdentityChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,7 +632,7 @@ QByteArray AvatarData::identityByteArray() {
|
||||||
QByteArray identityData;
|
QByteArray identityData;
|
||||||
QDataStream identityStream(&identityData, QIODevice::Append);
|
QDataStream identityStream(&identityData, QIODevice::Append);
|
||||||
|
|
||||||
identityStream << QUuid() << _faceModelURL << _skeletonModelURL << _displayName;
|
identityStream << QUuid() << _faceModelURL << _skeletonModelURL << _attachmentData << _displayName;
|
||||||
|
|
||||||
return identityData;
|
return identityData;
|
||||||
}
|
}
|
||||||
|
@ -654,6 +660,10 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||||
updateJointMappings();
|
updateJointMappings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarData::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||||
|
_attachmentData = attachmentData;
|
||||||
|
}
|
||||||
|
|
||||||
void AvatarData::setDisplayName(const QString& displayName) {
|
void AvatarData::setDisplayName(const QString& displayName) {
|
||||||
_displayName = displayName;
|
_displayName = displayName;
|
||||||
|
|
||||||
|
@ -762,3 +772,23 @@ void AvatarData::updateJointMappings() {
|
||||||
connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply()));
|
connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AttachmentData::AttachmentData() :
|
||||||
|
scale(1.0f) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AttachmentData::operator==(const AttachmentData& other) const {
|
||||||
|
return modelURL == other.modelURL && jointName == other.jointName && translation == other.translation &&
|
||||||
|
rotation == other.rotation && scale == other.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment) {
|
||||||
|
return out << attachment.modelURL << attachment.jointName <<
|
||||||
|
attachment.translation << attachment.rotation << attachment.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) {
|
||||||
|
return in >> attachment.modelURL >> attachment.jointName >>
|
||||||
|
attachment.translation >> attachment.rotation >> attachment.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,8 @@ typedef unsigned long long quint64;
|
||||||
|
|
||||||
#include <CollisionInfo.h>
|
#include <CollisionInfo.h>
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
#include <Node.h>
|
#include <Node.h>
|
||||||
|
|
||||||
#include "HeadData.h"
|
#include "HeadData.h"
|
||||||
|
@ -95,8 +97,10 @@ enum KeyState {
|
||||||
|
|
||||||
const glm::vec3 vec3Zero(0.0f);
|
const glm::vec3 vec3Zero(0.0f);
|
||||||
|
|
||||||
|
class QDataStream;
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
|
|
||||||
|
class AttachmentData;
|
||||||
class JointData;
|
class JointData;
|
||||||
|
|
||||||
class AvatarData : public QObject {
|
class AvatarData : public QObject {
|
||||||
|
@ -226,9 +230,11 @@ public:
|
||||||
const QUrl& getFaceModelURL() const { return _faceModelURL; }
|
const QUrl& getFaceModelURL() const { return _faceModelURL; }
|
||||||
QString getFaceModelURLString() const { return _faceModelURL.toString(); }
|
QString getFaceModelURLString() const { return _faceModelURL.toString(); }
|
||||||
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; }
|
||||||
|
const QVector<AttachmentData>& getAttachmentData() const { return _attachmentData; }
|
||||||
const QString& getDisplayName() const { return _displayName; }
|
const QString& getDisplayName() const { return _displayName; }
|
||||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||||
|
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||||
virtual void setDisplayName(const QString& displayName);
|
virtual void setDisplayName(const QString& displayName);
|
||||||
|
|
||||||
virtual void setBillboard(const QByteArray& billboard);
|
virtual void setBillboard(const QByteArray& billboard);
|
||||||
|
@ -291,6 +297,7 @@ protected:
|
||||||
|
|
||||||
QUrl _faceModelURL;
|
QUrl _faceModelURL;
|
||||||
QUrl _skeletonModelURL;
|
QUrl _skeletonModelURL;
|
||||||
|
QVector<AttachmentData> _attachmentData;
|
||||||
QString _displayName;
|
QString _displayName;
|
||||||
|
|
||||||
QRect _displayNameBoundingRect;
|
QRect _displayNameBoundingRect;
|
||||||
|
@ -325,4 +332,20 @@ public:
|
||||||
glm::quat rotation;
|
glm::quat rotation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AttachmentData {
|
||||||
|
public:
|
||||||
|
QUrl modelURL;
|
||||||
|
QString jointName;
|
||||||
|
glm::vec3 translation;
|
||||||
|
glm::quat rotation;
|
||||||
|
float scale;
|
||||||
|
|
||||||
|
AttachmentData();
|
||||||
|
|
||||||
|
bool operator==(const AttachmentData& other) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment);
|
||||||
|
QDataStream& operator>>(QDataStream& in, AttachmentData& attachment);
|
||||||
|
|
||||||
#endif // hifi_AvatarData_h
|
#endif // hifi_AvatarData_h
|
||||||
|
|
|
@ -127,8 +127,9 @@ void AvatarHashMap::processAvatarIdentityPacket(const QByteArray &packet, const
|
||||||
while (!identityStream.atEnd()) {
|
while (!identityStream.atEnd()) {
|
||||||
|
|
||||||
QUrl faceMeshURL, skeletonURL;
|
QUrl faceMeshURL, skeletonURL;
|
||||||
|
QVector<AttachmentData> attachmentData;
|
||||||
QString displayName;
|
QString displayName;
|
||||||
identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> displayName;
|
identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName;
|
||||||
|
|
||||||
// mesh URL for a UUID, find avatar in our list
|
// mesh URL for a UUID, find avatar in our list
|
||||||
AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
||||||
|
@ -142,6 +143,10 @@ void AvatarHashMap::processAvatarIdentityPacket(const QByteArray &packet, const
|
||||||
matchingAvatar->setSkeletonModelURL(skeletonURL);
|
matchingAvatar->setSkeletonModelURL(skeletonURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (matchingAvatar->getAttachmentData() != attachmentData) {
|
||||||
|
matchingAvatar->setAttachmentData(attachmentData);
|
||||||
|
}
|
||||||
|
|
||||||
if (matchingAvatar->getDisplayName() != displayName) {
|
if (matchingAvatar->getDisplayName() != displayName) {
|
||||||
matchingAvatar->setDisplayName(displayName);
|
matchingAvatar->setDisplayName(displayName);
|
||||||
}
|
}
|
||||||
|
@ -171,4 +176,4 @@ void AvatarHashMap::processKillAvatar(const QByteArray& datagram) {
|
||||||
if (matchedAvatar != _avatarHash.end()) {
|
if (matchedAvatar != _avatarHash.end()) {
|
||||||
erase(matchedAvatar);
|
erase(matchedAvatar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -817,7 +817,7 @@ ExtractedMesh extractMesh(const FBXNode& object) {
|
||||||
while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0);
|
while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0);
|
||||||
|
|
||||||
QPair<int, int> materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0,
|
QPair<int, int> materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0,
|
||||||
(polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0);
|
(polygonIndex < textures.size()) ? textures.at(polygonIndex) : -1);
|
||||||
int& partIndex = materialTextureParts[materialTexture];
|
int& partIndex = materialTextureParts[materialTexture];
|
||||||
if (partIndex == 0) {
|
if (partIndex == 0) {
|
||||||
data.extracted.partMaterialTextures.append(materialTexture);
|
data.extracted.partMaterialTextures.append(materialTexture);
|
||||||
|
|
|
@ -46,7 +46,7 @@ const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 2048;
|
||||||
const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE;
|
const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE;
|
||||||
const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container
|
const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container
|
||||||
const QString MODEL_DEFAULT_MODEL_URL("");
|
const QString MODEL_DEFAULT_MODEL_URL("");
|
||||||
const glm::quat MODEL_DEFAULT_MODEL_ROTATION(0, 0, 0, 0);
|
const glm::quat MODEL_DEFAULT_MODEL_ROTATION;
|
||||||
|
|
||||||
/// A collection of properties of a model item used in the scripting API. Translates between the actual properties of a model
|
/// A collection of properties of a model item used in the scripting API. Translates between the actual properties of a model
|
||||||
/// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of
|
/// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of
|
||||||
|
|
|
@ -49,6 +49,8 @@ PacketVersion versionForPacketType(PacketType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PacketTypeAvatarData:
|
case PacketTypeAvatarData:
|
||||||
return 3;
|
return 3;
|
||||||
|
case PacketTypeAvatarIdentity:
|
||||||
|
return 1;
|
||||||
case PacketTypeEnvironmentData:
|
case PacketTypeEnvironmentData:
|
||||||
return 1;
|
return 1;
|
||||||
case PacketTypeParticleData:
|
case PacketTypeParticleData:
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
const float DEFAULT_KEYHOLE_RADIUS = 3.0f;
|
const float DEFAULT_KEYHOLE_RADIUS = 3.0f;
|
||||||
const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f;
|
const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f;
|
||||||
|
const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.f;
|
||||||
const float DEFAULT_ASPECT_RATIO = 16.f/9.f;
|
const float DEFAULT_ASPECT_RATIO = 16.f/9.f;
|
||||||
const float DEFAULT_NEAR_CLIP = 0.08f;
|
const float DEFAULT_NEAR_CLIP = 0.08f;
|
||||||
const float DEFAULT_FAR_CLIP = 50.0f * TREE_SCALE;
|
const float DEFAULT_FAR_CLIP = 50.0f * TREE_SCALE;
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
#include "StreamUtils.h"
|
#include "StreamUtils.h"
|
||||||
|
@ -47,6 +49,22 @@ std::ostream& operator<<(std::ostream& s, const glm::mat4& m) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& out, const glm::vec3& vector) {
|
||||||
|
return out << vector.x << vector.y << vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator>>(QDataStream& in, glm::vec3& vector) {
|
||||||
|
return in >> vector.x >> vector.y >> vector.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& out, const glm::quat& quaternion) {
|
||||||
|
return out << quaternion.x << quaternion.y << quaternion.z << quaternion.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator>>(QDataStream& in, glm::quat& quaternion) {
|
||||||
|
return in >> quaternion.x >> quaternion.y >> quaternion.z >> quaternion.w;
|
||||||
|
}
|
||||||
|
|
||||||
// less common utils can be enabled with DEBUG
|
// less common utils can be enabled with DEBUG
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
|
class QDataStream;
|
||||||
|
|
||||||
namespace StreamUtil {
|
namespace StreamUtil {
|
||||||
// dump the buffer, 32 bytes per row, each byte in hex, separated by whitespace
|
// dump the buffer, 32 bytes per row, each byte in hex, separated by whitespace
|
||||||
|
@ -29,6 +30,12 @@ std::ostream& operator<<(std::ostream& s, const glm::vec3& v);
|
||||||
std::ostream& operator<<(std::ostream& s, const glm::quat& q);
|
std::ostream& operator<<(std::ostream& s, const glm::quat& q);
|
||||||
std::ostream& operator<<(std::ostream& s, const glm::mat4& m);
|
std::ostream& operator<<(std::ostream& s, const glm::mat4& m);
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& out, const glm::vec3& vector);
|
||||||
|
QDataStream& operator>>(QDataStream& in, glm::vec3& vector);
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& out, const glm::quat& quaternion);
|
||||||
|
QDataStream& operator>>(QDataStream& in, glm::quat& quaternion);
|
||||||
|
|
||||||
// less common utils can be enabled with DEBUG
|
// less common utils can be enabled with DEBUG
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#include "CollisionInfo.h"
|
#include "CollisionInfo.h"
|
||||||
|
|
Loading…
Reference in a new issue