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

This commit is contained in:
Thijs Wenker 2014-04-21 05:26:42 +02:00
commit 23068b0356
23 changed files with 880 additions and 595 deletions

View file

@ -847,8 +847,9 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
// this is a script upload - ask the HTTPConnection to parse the form data
QList<FormData> formData = connection->parseFormData();
// check how many instances of this assignment the user wants by checking the ASSIGNMENT-INSTANCES header
// check optional headers for # of instances and pool
const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES";
const QString ASSIGNMENT_POOL_HEADER = "ASSIGNMENT-POOL";
QByteArray assignmentInstancesValue = connection->requestHeaders().value(ASSIGNMENT_INSTANCES_HEADER.toLocal8Bit());
@ -861,24 +862,33 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
numInstances = assignmentInstancesValue.toInt();
}
QString assignmentPool = emptyPool;
QByteArray assignmentPoolValue = connection->requestHeaders().value(ASSIGNMENT_POOL_HEADER.toLocal8Bit());
if (!assignmentPoolValue.isEmpty()) {
// specific pool requested, set that on the created assignment
assignmentPool = QString(assignmentPoolValue);
}
const char ASSIGNMENT_SCRIPT_HOST_LOCATION[] = "resources/web/assignment";
for (int i = 0; i < numInstances; i++) {
// create an assignment for this saved script
Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType);
Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType, assignmentPool);
QString newPath(ASSIGNMENT_SCRIPT_HOST_LOCATION);
newPath += "/";
// append the UUID for this script as the new filename, remove the curly braces
newPath += uuidStringWithoutCurlyBraces(scriptAssignment->getUUID());
// create a file with the GUID of the assignment in the script host locaiton
// create a file with the GUID of the assignment in the script host location
QFile scriptFile(newPath);
scriptFile.open(QIODevice::WriteOnly);
scriptFile.write(formData[0].second);
qDebug("Saved a script for assignment at %s", qPrintable(newPath));
qDebug() << qPrintable(QString("Saved a script for assignment at %1%2")
.arg(newPath).arg(assignmentPool == emptyPool ? "" : " - pool is " + assignmentPool));
// add the script assigment to the assignment queue
_assignmentQueue.enqueue(SharedAssignmentPointer(scriptAssignment));

View file

@ -27,7 +27,7 @@ Script.update.connect(function(deltaTime) {
if (!jointMapping) {
var avatarJointNames = Avatar.jointNames;
var animationJointNames = animation.jointNames;
if (avatarJointNames === 0 || animationJointNames.length === 0) {
if (avatarJointNames.length === 0 || animationJointNames.length === 0) {
return;
}
jointMapping = new Array(avatarJointNames.length);

View file

@ -19,7 +19,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var editToolsOn = true; // starts out off
var windowDimensions = Controller.getViewportDimensions();
var WORLD_SCALE = Voxels.getTreeScale();
var NEW_VOXEL_SIZE = 1.0;
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
@ -62,7 +65,7 @@ colors[6] = { red: 211, green: 115, blue: 0 };
colors[7] = { red: 48, green: 116, blue: 119 };
colors[8] = { red: 31, green: 64, blue: 64 };
var numColors = 9;
var whichColor = -1; // Starting color is 'Copy' mode
var whichColor = 0; // Starting color is 'Copy' mode
// Create sounds for for every script actions that require one
var audioOptions = new AudioInjectionOptions();
@ -149,9 +152,6 @@ colorInheritSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/
colorInheritSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Color+Inherit/Inherit+B.raw");
colorInheritSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Color+Inherit/Inherit+C.raw");
var editToolsOn = true; // starts out off
// previewAsVoxel - by default, we will preview adds/deletes/recolors as just 4 lines on the intersecting face. But if you
// the preview to show a full voxel then set this to true and the voxel will be displayed for voxel editing
var previewAsVoxel = false;
@ -204,8 +204,8 @@ var linePreviewRight = Overlays.addOverlay("line3d", {
// these will be used below
var sliderWidth = 154;
var sliderHeight = 37;
var scaleSelectorWidth = 144;
var scaleSelectorHeight = 37;
// These will be our "overlay IDs"
var swatches = new Array();
@ -213,7 +213,7 @@ var swatchExtraPadding = 5;
var swatchHeight = 37;
var swatchWidth = 27;
var swatchesWidth = swatchWidth * numColors + numColors + swatchExtraPadding * 2;
var swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
var swatchesX = (windowDimensions.x - (swatchesWidth + scaleSelectorWidth)) / 2;
var swatchesY = windowDimensions.y - swatchHeight + 1;
var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/";
@ -270,7 +270,7 @@ var voxelTool = 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",
visible: false,
visible: editToolsOn,
alpha: 0.9
});
@ -278,7 +278,7 @@ var recolorTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
imageURL: toolIconUrl + "paint-tool.svg",
visible: false,
visible: editToolsOn,
alpha: 0.9
});
@ -286,58 +286,154 @@ var eyedropperTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
imageURL: toolIconUrl + "eyedropper-tool.svg",
visible: false,
visible: editToolsOn,
alpha: 0.9
});
// This will create a couple of image overlays that make a "slider", we will demonstrate how to trap mouse messages to
// move the slider
// see above...
//var sliderWidth = 158;
//var sliderHeight = 35;
var copyScale = true;
function ScaleSelector() {
this.x = swatchesX + swatchesWidth;
this.y = swatchesY;
this.width = scaleSelectorWidth;
this.height = scaleSelectorHeight;
var sliderOffsetX = 17;
var sliderX = swatchesX - swatchWidth - sliderOffsetX;
var sliderY = windowDimensions.y - sliderHeight + 1;
var slider = Overlays.addOverlay("image", {
// alternate form of expressing bounds
bounds: { x: sliderX, y: sliderY, width: sliderWidth, height: sliderHeight},
imageURL: toolIconUrl + "voxel-size-slider-bg.svg",
alpha: 1,
this.displayPower = false;
this.scale = 1.0;
this.power = 0;
this.FIRST_PART = this.width * 40.0 / 100.0;
this.SECOND_PART = this.width * 37.0 / 100.0;
this.buttonsOverlay = Overlays.addOverlay("image", {
x: this.x, y: this.y,
width: this.width, height: this.height,
//subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
imageURL: toolIconUrl + "voxel-size-selector.svg",
alpha: 0.9,
visible: editToolsOn
});
this.textOverlay = Overlays.addOverlay("text", {
x: this.x + this.FIRST_PART, y: this.y,
width: this.SECOND_PART, height: this.height,
topMargin: 13,
text: this.scale.toString(),
alpha: 0.0,
visible: editToolsOn
});
this.powerOverlay = Overlays.addOverlay("text", {
x: this.x + this.FIRST_PART, y: this.y,
width: this.SECOND_PART, height: this.height,
leftMargin: 28,
text: this.power.toString(),
alpha: 0.0,
visible: false
});
this.setScale = function(scale) {
this.scale = scale;
this.power = Math.floor(Math.log(scale));
rescaleImport();
}
// The slider is handled in the mouse event callbacks.
var isMovingSlider = false;
var thumbClickOffsetX = 0;
this.show = function(doShow) {
Overlays.editOverlay(this.buttonsOverlay, {visible: doShow});
Overlays.editOverlay(this.textOverlay, {visible: doShow});
Overlays.editOverlay(this.powerOverlay, {visible: doShow && this.displayPower});
}
// This is the thumb of our slider
var minThumbX = 20; // relative to the x of the slider
var maxThumbX = minThumbX + 90;
var thumbExtents = maxThumbX - minThumbX;
var thumbX = (minThumbX + maxThumbX) / 2;
var thumbOffsetY = 11;
var thumbY = sliderY + thumbOffsetY;
var thumb = Overlays.addOverlay("image", {
x: sliderX + thumbX,
y: thumbY,
width: 17,
height: 17,
imageURL: toolIconUrl + "voxel-size-slider-handle.svg",
alpha: 1,
this.move = function() {
this.x = swatchesX + swatchesWidth;
this.y = swatchesY;
Overlays.editOverlay(this.buttonsOverlay, {
x: this.x, y: this.y,
});
Overlays.editOverlay(this.textOverlay, {
x: this.x + this.FIRST_PART, y: this.y,
});
Overlays.editOverlay(this.powerOverlay, {
x: this.x + this.FIRST_PART, y: this.y,
});
}
this.switchDisplay = function() {
this.displayPower = !this.displayPower;
if (this.displayPower) {
Overlays.editOverlay(this.textOverlay, {
leftMargin: 18,
text: "2"
});
Overlays.editOverlay(this.powerOverlay, {
text: this.power.toString(),
visible: editToolsOn
});
} else {
Overlays.editOverlay(this.textOverlay, {
leftMargin: 13,
text: this.scale.toString()
});
Overlays.editOverlay(this.powerOverlay, {
visible: false
});
}
}
var pointerVoxelScale = Math.floor(MAX_VOXEL_SCALE + MIN_VOXEL_SCALE) / 2; // this is the voxel scale used for click to add or delete
var pointerVoxelScaleSet = false; // if voxel scale has not yet been set, we use the intersection size
this.update = function() {
if (this.displayPower) {
Overlays.editOverlay(this.powerOverlay, {text: this.power.toString()});
} else {
Overlays.editOverlay(this.textOverlay, {text: this.scale.toString()});
}
}
var pointerVoxelScaleSteps = 8; // the number of slider position steps
var pointerVoxelScaleOriginStep = 8; // the position of slider for the 1 meter size voxel
var pointerVoxelScaleMin = Math.pow(2, (1-pointerVoxelScaleOriginStep));
var pointerVoxelScaleMax = Math.pow(2, (pointerVoxelScaleSteps-pointerVoxelScaleOriginStep));
var thumbDeltaPerStep = thumbExtents / (pointerVoxelScaleSteps - 1);
this.incrementScale = function() {
copyScale = false;
if (this.power < 13) {
++this.power;
this.scale *= 2.0;
this.update();
rescaleImport();
resizeVoxelSound.play(voxelSizePlus);
}
}
this.decrementScale = function() {
copyScale = false;
if (-4 < this.power) {
--this.power;
this.scale /= 2.0;
this.update();
rescaleImport();
resizeVoxelSound.play(voxelSizePlus);
}
}
this.clicked = function(x, y) {
if (this.x < x && x < this.x + this.width &&
this.y < y && y < this.y + this.height) {
if (x < this.x + this.FIRST_PART) {
this.decrementScale();
} else if (x < this.x + this.FIRST_PART + this.SECOND_PART) {
this.switchDisplay();
} else {
this.incrementScale();
}
return true;
}
return false;
}
this.cleanup = function() {
Overlays.deleteOverlay(this.buttonsOverlay);
Overlays.deleteOverlay(this.textOverlay);
Overlays.deleteOverlay(this.powerOverlay);
}
}
var scaleSelector = new ScaleSelector();
///////////////////////////////////// IMPORT MODULE ///////////////////////////////
@ -345,9 +441,12 @@ var thumbDeltaPerStep = thumbExtents / (pointerVoxelScaleSteps - 1);
var importTree;
var importPreview;
var importBoundaries;
var xImportGuide;
var yImportGuide;
var zImportGuide;
var isImporting;
var importPosition;
var importScale;
var importDistance;
function initImport() {
importPreview = Overlays.addOverlay("localvoxels", {
@ -358,31 +457,49 @@ function initImport() {
});
importBoundaries = Overlays.addOverlay("cube", {
position: { x: 0, y: 0, z: 0 },
scale: 1,
size: 1,
color: { red: 128, blue: 128, green: 128 },
lineWIdth: 4,
solid: false,
visible: false
})
});
xImportGuide = Overlays.addOverlay("line3d", {
position: { x: 0, y: 0, z: 0},
end: { x: 0, y: 0, z: 0},
color: { red: 255, green: 0, blue: 0},
alpha: 1,
visible: false,
lineWidth: previewLineWidth
});
yImportGuide = Overlays.addOverlay("line3d", {
position: { x: 0, y: 0, z: 0},
end: { x: 0, y: 0, z: 0},
color: { red: 0, green: 255, blue: 0},
alpha: 1,
visible: false,
lineWidth: previewLineWidth
});
zImportGuide = Overlays.addOverlay("line3d", {
position: { x: 0, y: 0, z: 0},
end: { x: 0, y: 0, z: 0},
color: { red: 0, green: 0, blue: 255},
alpha: 1,
visible: false,
lineWidth: previewLineWidth
});
isImporting = false;
importPosition = { x: 0, y: 0, z: 0 };
importScale = 0;
}
function importVoxels() {
if (Clipboard.importVoxels()) {
isImporting = true;
if (importScale <= 0) {
importScale = 1;
}
} else {
isImporting = false;
}
isImporting = Clipboard.importVoxels();
return isImporting;
}
function moveImport(position) {
if (0 < position.x && 0 < position.y && 0 < position.z) {
importPosition = position;
Overlays.editOverlay(importPreview, {
position: { x: importPosition.x, y: importPosition.y, z: importPosition.z }
@ -390,17 +507,40 @@ function moveImport(position) {
Overlays.editOverlay(importBoundaries, {
position: { x: importPosition.x, y: importPosition.y, z: importPosition.z }
});
}
Overlays.editOverlay(xImportGuide, {
position: { x: importPosition.x, y: 0, z: importPosition.z },
end: { x: importPosition.x + scaleSelector.scale, y: 0, z: importPosition.z }
});
Overlays.editOverlay(yImportGuide, {
position: { x: importPosition.x, y: importPosition.y, z: importPosition.z },
end: { x: importPosition.x, y: 0, z: importPosition.z }
});
Overlays.editOverlay(zImportGuide, {
position: { x: importPosition.x, y: 0, z: importPosition.z },
end: { x: importPosition.x, y: 0, z: importPosition.z + scaleSelector.scale }
});
rescaleImport();
}
function rescaleImport(scale) {
if (0 < scale) {
importScale = scale;
function rescaleImport() {
if (0 < scaleSelector.scale) {
Overlays.editOverlay(importPreview, {
scale: importScale
scale: scaleSelector.scale
});
Overlays.editOverlay(importBoundaries, {
scale: importScale
size: scaleSelector.scale
});
Overlays.editOverlay(xImportGuide, {
end: { x: importPosition.x + scaleSelector.scale, y: 0, z: importPosition.z }
});
Overlays.editOverlay(yImportGuide, {
end: { x: importPosition.x, y: 0, z: importPosition.z }
});
Overlays.editOverlay(zImportGuide, {
end: { x: importPosition.x, y: 0, z: importPosition.z + scaleSelector.scale }
});
}
}
@ -412,11 +552,21 @@ function showImport(doShow) {
Overlays.editOverlay(importBoundaries, {
visible: doShow
});
Overlays.editOverlay(xImportGuide, {
visible: doShow
});
Overlays.editOverlay(yImportGuide, {
visible: doShow
});
Overlays.editOverlay(zImportGuide, {
visible: doShow
});
}
function placeImport() {
if (isImporting) {
Clipboard.pasteVoxel(importPosition.x, importPosition.y, importPosition.z, importScale);
Clipboard.pasteVoxel(importPosition.x, importPosition.y, importPosition.z, scaleSelector.scale);
isImporting = false;
}
}
@ -431,9 +581,11 @@ function cancelImport() {
function cleanupImport() {
Overlays.deleteOverlay(importPreview);
Overlays.deleteOverlay(importBoundaries);
Overlays.deleteOverlay(xImportGuide);
Overlays.deleteOverlay(yImportGuide);
Overlays.deleteOverlay(zImportGuide);
isImporting = false;
importPostion = { x: 0, y: 0, z: 0 };
importScale = 0;
}
/////////////////////////////////// END IMPORT MODULE /////////////////////////////
initImport();
@ -442,49 +594,6 @@ if (editToolsOn) {
moveTools();
}
function calcThumbFromScale(scale) {
var scaleLog = Math.log(scale)/Math.log(2);
var thumbStep = scaleLog + pointerVoxelScaleOriginStep;
if (thumbStep < 1) {
thumbStep = 1;
}
if (thumbStep > pointerVoxelScaleSteps) {
thumbStep = pointerVoxelScaleSteps;
}
var oldThumbX = thumbX;
thumbX = (thumbDeltaPerStep * (thumbStep - 1)) + minThumbX;
Overlays.editOverlay(thumb, { x: thumbX + sliderX } );
if (thumbX > oldThumbX) {
resizeVoxelSound.play(voxelSizePlus);
print("Plus");
} else if (thumbX < oldThumbX) {
resizeVoxelSound.play(voxelSizeMinus);
print("Minus");
}
}
function calcScaleFromThumb(newThumbX) {
// newThumbX is the pixel location relative to start of slider,
// we need to figure out the actual offset in the allowed slider area
thumbAt = newThumbX - minThumbX;
thumbStep = Math.floor((thumbAt/ thumbExtents) * (pointerVoxelScaleSteps-1)) + 1;
pointerVoxelScale = Math.pow(2, (thumbStep-pointerVoxelScaleOriginStep));
// if importing, rescale import ...
if (isImporting) {
var importScale = (pointerVoxelScale / MAX_VOXEL_SCALE) * MAX_PASTE_VOXEL_SCALE;
rescaleImport(importScale);
}
// now reset the display accordingly...
calcThumbFromScale(pointerVoxelScale);
// if the user moved the thumb, then they are fixing the voxel scale
pointerVoxelScaleSet = true;
}
function setAudioPosition() {
var position = MyAvatar.position;
var forwardVector = Quat.getFront(MyAvatar.orientation);
@ -493,7 +602,7 @@ function setAudioPosition() {
function getNewPasteVoxel(pickRay) {
var voxelSize = MIN_PASTE_VOXEL_SCALE + (MAX_PASTE_VOXEL_SCALE - MIN_PASTE_VOXEL_SCALE) * pointerVoxelScale - 1;
var voxelSize = scaleSelector.scale;
var origin = { x: pickRay.direction.x, y: pickRay.direction.y, z: pickRay.direction.z };
origin.x += pickRay.origin.x;
@ -542,13 +651,7 @@ function calculateVoxelFromIntersection(intersection, operation) {
+ intersection.intersection.y + ", " + intersection.intersection.z);
}
var voxelSize;
if (pointerVoxelScaleSet) {
voxelSize = pointerVoxelScale;
} else {
voxelSize = intersection.voxel.s;
}
var voxelSize = scaleSelector.scale;
var x;
var y;
var z;
@ -670,21 +773,9 @@ function showPreviewVoxel() {
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
var intersection = Voxels.findRayIntersection(pickRay);
// if the user hasn't updated the
if (!pointerVoxelScaleSet) {
calcThumbFromScale(intersection.voxel.s);
}
if (whichColor == -1) {
// Copy mode - use clicked voxel color
voxelColor = { red: intersection.voxel.red,
green: intersection.voxel.green,
blue: intersection.voxel.blue };
} else {
voxelColor = { red: colors[whichColor].red,
green: colors[whichColor].green,
blue: colors[whichColor].blue };
}
var guidePosition;
if (trackAsRecolor || recolorToolSelected || trackAsEyedropper || eyedropperToolSelected) {
@ -734,18 +825,19 @@ function showPreviewLines() {
var intersection = Voxels.findRayIntersection(pickRay);
if (intersection.intersects) {
// if the user hasn't updated the
if (!pointerVoxelScaleSet) {
calcThumbFromScale(intersection.voxel.s);
}
resultVoxel = calculateVoxelFromIntersection(intersection,"");
Overlays.editOverlay(voxelPreview, { visible: false });
Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true });
Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true });
Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true });
Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true });
colors[0] = {red: intersection.voxel.red, green: intersection.voxel.green , blue: intersection.voxel.blue };
if (copyScale) {
scaleSelector.setScale(intersection.voxel.s);
scaleSelector.update();
}
moveTools();
} else {
Overlays.editOverlay(voxelPreview, { visible: false });
Overlays.editOverlay(linePreviewTop, { visible: false });
@ -756,7 +848,7 @@ function showPreviewLines() {
}
function showPreviewGuides() {
if (editToolsOn) {
if (editToolsOn && !isImporting) {
if (previewAsVoxel) {
showPreviewVoxel();
@ -817,6 +909,7 @@ function trackKeyReleaseEvent(event) {
moveTools();
setAudioPosition(); // make sure we set the audio position before playing sounds
showPreviewGuides();
scaleSelector.show(editToolsOn);
scriptInitSound.playRandom();
}
@ -826,17 +919,14 @@ function trackKeyReleaseEvent(event) {
if (editToolsOn) {
if (event.text == "ESC") {
pointerVoxelScaleSet = false;
pasteMode = false;
moveTools();
}
if (event.text == "-") {
thumbX -= thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
scaleSelector.decrementScale();
}
if (event.text == "+") {
thumbX += thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
scaleSelector.incrementScale();
}
if (event.text == "CONTROL") {
trackAsDelete = false;
@ -872,15 +962,7 @@ function mousePressEvent(event) {
var clickedOnSomething = false;
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
// If the user clicked on the thumb, handle the slider logic
if (clickedOverlay == thumb) {
isMovingSlider = true;
thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb
clickedOnSomething = true;
Overlays.editOverlay(thumb, { imageURL: toolIconUrl + "voxel-size-slider-handle.svg", });
} else if (clickedOverlay == voxelTool) {
if (clickedOverlay == voxelTool) {
modeSwitchSound.play(0);
voxelToolSelected = true;
recolorToolSelected = false;
@ -901,19 +983,10 @@ function mousePressEvent(event) {
eyedropperToolSelected = true;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == slider) {
if (event.x < sliderX + minThumbX) {
thumbX -= thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
} else if (scaleSelector.clicked(event.x, event.y)) {
if (isImporting) {
rescaleImport();
}
if (event.x > sliderX + maxThumbX) {
thumbX += thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
}
moveTools();
clickedOnSomething = true;
} else {
// if the user clicked on one of the color swatches, update the selectedSwatch
@ -927,7 +1000,7 @@ function mousePressEvent(event) {
}
}
}
if (clickedOnSomething) {
if (clickedOnSomething || isImporting) {
return; // no further processing
}
@ -939,14 +1012,6 @@ function mousePressEvent(event) {
var intersection = Voxels.findRayIntersection(pickRay);
audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction);
if (isImporting) {
print("placing import...");
placeImport();
showImport(false);
moveTools();
return;
}
if (pasteMode) {
var pasteVoxel = getNewPasteVoxel(pickRay);
Clipboard.pasteVoxel(pasteVoxel.origin.x, pasteVoxel.origin.y, pasteVoxel.origin.z, pasteVoxel.voxelSize);
@ -956,11 +1021,6 @@ function mousePressEvent(event) {
}
if (intersection.intersects) {
// if the user hasn't updated the
if (!pointerVoxelScaleSet) {
calcThumbFromScale(intersection.voxel.s);
}
if (trackAsDelete || event.isRightButton && !trackAsEyedropper) {
// Delete voxel
voxelDetails = calculateVoxelFromIntersection(intersection,"delete");
@ -968,13 +1028,11 @@ function mousePressEvent(event) {
delVoxelSound.playRandom();
Overlays.editOverlay(voxelPreview, { visible: false });
} else if (eyedropperToolSelected || trackAsEyedropper) {
if (whichColor != -1) {
colors[whichColor].red = intersection.voxel.red;
colors[whichColor].green = intersection.voxel.green;
colors[whichColor].blue = intersection.voxel.blue;
moveTools();
swatchesSound.play(whichColor);
}
} else if (recolorToolSelected || trackAsRecolor) {
// Recolor Voxel
@ -987,18 +1045,10 @@ function mousePressEvent(event) {
Overlays.editOverlay(voxelPreview, { visible: false });
} else if (voxelToolSelected) {
// Add voxel on face
if (whichColor == -1) {
// Copy mode - use clicked voxel color
newColor = {
red: intersection.voxel.red,
green: intersection.voxel.green,
blue: intersection.voxel.blue };
} else {
newColor = {
red: colors[whichColor].red,
newColor = { red: colors[whichColor].red,
green: colors[whichColor].green,
blue: colors[whichColor].blue };
}
blue: colors[whichColor].blue
};
voxelDetails = calculateVoxelFromIntersection(intersection,"add");
Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s,
@ -1021,10 +1071,7 @@ function keyPressEvent(event) {
if (editToolsOn) {
var nVal = parseInt(event.text);
if (event.text == "`") {
print("Color = Copy");
whichColor = -1;
colorInheritSound.playRandom();
moveTools();
copyScale = true;
} else if ((nVal > 0) && (nVal <= numColors)) {
whichColor = nVal - 1;
print("Color = " + (whichColor + 1));
@ -1032,17 +1079,15 @@ function keyPressEvent(event) {
moveTools();
} else if (event.text == "0") {
// Create a brand new 1 meter voxel in front of your avatar
var color = whichColor;
if (color == -1) color = 0;
var newPosition = getNewVoxelPosition();
var newVoxel = {
x: newPosition.x,
y: newPosition.y ,
z: newPosition.z,
s: NEW_VOXEL_SIZE,
red: colors[color].red,
green: colors[color].green,
blue: colors[color].blue };
red: colors[whichColor].red,
green: colors[whichColor].green,
blue: colors[whichColor].blue };
Voxels.eraseVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s);
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
setAudioPosition();
@ -1158,17 +1203,44 @@ function mouseMoveEvent(event) {
return;
}
if (isMovingSlider) {
thumbX = (event.x - thumbClickOffsetX) - sliderX;
if (thumbX < minThumbX) {
thumbX = minThumbX;
}
if (thumbX > maxThumbX) {
thumbX = maxThumbX;
}
calcScaleFromThumb(thumbX);
// Move Import Preview
if (isImporting) {
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Voxels.findRayIntersection(pickRay);
} else if (isAdding) {
var distance = 2 * scaleSelector.scale;
if (intersection.intersects) {
var intersectionDistance = Vec3.length(Vec3.subtract(pickRay.origin, intersection.intersection));
if (intersectionDistance < distance) {
distance = intersectionDistance * 0.99;
}
}
var targetPosition = { x: pickRay.direction.x * distance,
y: pickRay.direction.y * distance,
z: pickRay.direction.z * distance
};
targetPosition.x += pickRay.origin.x;
targetPosition.y += pickRay.origin.y;
targetPosition.z += pickRay.origin.z;
if (targetPosition.x < 0) targetPosition.x = 0;
if (targetPosition.y < 0) targetPosition.y = 0;
if (targetPosition.z < 0) targetPosition.z = 0;
var nudgeFactor = scaleSelector.scale;
var newPosition = {
x: Math.floor(targetPosition.x / nudgeFactor) * nudgeFactor,
y: Math.floor(targetPosition.y / nudgeFactor) * nudgeFactor,
z: Math.floor(targetPosition.z / nudgeFactor) * nudgeFactor
}
moveImport(newPosition);
}
if (isAdding) {
// Watch the drag direction to tell which way to 'extrude' this voxel
if (!isExtruding) {
var pickRay = Camera.computePickRay(event.x, event.y);
@ -1220,16 +1292,13 @@ function mouseReleaseEvent(event) {
return;
}
if (isMovingSlider) {
isMovingSlider = false;
}
isAdding = false;
isExtruding = false;
}
function moveTools() {
// move the swatches
swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
swatchesX = (windowDimensions.x - (swatchesWidth + scaleSelectorWidth)) / 2;
swatchesY = windowDimensions.y - swatchHeight + 1;
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
@ -1302,14 +1371,7 @@ function moveTools() {
visible: editToolsOn
});
sliderX = swatchesX + swatchesWidth - sliderOffsetX;
sliderY = windowDimensions.y - sliderHeight + 1;
thumbY = sliderY + thumbOffsetY;
Overlays.editOverlay(slider, { x: sliderX, y: sliderY, visible: editToolsOn });
// This is the thumb of our slider
Overlays.editOverlay(thumb, { x: sliderX + thumbX, y: thumbY, visible: editToolsOn });
scaleSelector.move();
}
var lastFingerAddVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area
@ -1330,11 +1392,7 @@ function checkControllers() {
if (Controller.isButtonPressed(BUTTON_1)) {
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerAddVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
if (whichColor == -1) {
newColor = { red: colors[0].red, green: colors[0].green, blue: colors[0].blue };
} else {
newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue };
}
Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE);
Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE,
@ -1360,19 +1418,6 @@ function update(deltaTime) {
}
checkControllers();
// Move Import Preview
if (isImporting) {
var position = MyAvatar.position;
var forwardVector = Quat.getFront(MyAvatar.orientation);
var targetPosition = Vec3.sum(position, Vec3.multiply(forwardVector, importScale));
var newPosition = {
x: Math.floor(targetPosition.x / importScale) * importScale,
y: Math.floor(targetPosition.y / importScale) * importScale,
z: Math.floor(targetPosition.z / importScale) * importScale
}
moveImport(newPosition);
}
}
}
@ -1380,28 +1425,17 @@ function wheelEvent(event) {
wheelPixelsMoved += event.delta;
if (Math.abs(wheelPixelsMoved) > WHEEL_PIXELS_PER_SCALE_CHANGE)
{
if (!pointerVoxelScaleSet) {
pointerVoxelScale = 1.0;
pointerVoxelScaleSet = true;
}
if (wheelPixelsMoved > 0) {
pointerVoxelScale /= 2.0;
if (pointerVoxelScale < MIN_VOXEL_SCALE) {
pointerVoxelScale = MIN_VOXEL_SCALE;
}
scaleSelector.decrementScale();
} else {
pointerVoxelScale *= 2.0;
if (pointerVoxelScale > MAX_VOXEL_SCALE) {
pointerVoxelScale = MAX_VOXEL_SCALE;
scaleSelector.incrementScale();
}
}
calcThumbFromScale(pointerVoxelScale);
trackMouseEvent(event);
wheelPixelsMoved = 0;
if (isImporting) {
var importScale = (pointerVoxelScale / MAX_VOXEL_SCALE) * MAX_PASTE_VOXEL_SCALE;
rescaleImport(importScale);
rescaleImport();
}
}
}
@ -1428,11 +1462,10 @@ function scriptEnding() {
Overlays.deleteOverlay(voxelTool);
Overlays.deleteOverlay(recolorTool);
Overlays.deleteOverlay(eyedropperTool);
Overlays.deleteOverlay(slider);
Overlays.deleteOverlay(thumb);
Controller.releaseKeyEvents({ text: "+" });
Controller.releaseKeyEvents({ text: "-" });
cleanupImport();
scaleSelector.cleanup();
cleanupMenus();
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -356,6 +356,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
QMutexLocker locker(&_settingsMutex);
_previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString();
}
//When -url in command line, teleport to location
urlGoTo(argc, constArgv);
}
Application::~Application() {
@ -821,6 +823,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_E:
case Qt::Key_PageUp:
if (!_myAvatar->getDriveKeys(UP)) {
_myAvatar->jump();
}
@ -832,6 +835,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_C:
case Qt::Key_PageDown:
_myAvatar->setDriveKeys(DOWN, 1.f);
break;
@ -1020,10 +1024,12 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
switch (event->key()) {
case Qt::Key_E:
case Qt::Key_PageUp:
_myAvatar->setDriveKeys(UP, 0.f);
break;
case Qt::Key_C:
case Qt::Key_PageDown:
_myAvatar->setDriveKeys(DOWN, 0.f);
break;
@ -2735,7 +2741,7 @@ void Application::displayOverlay() {
(Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth))
? 80 : 20;
drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 1.0f, 0.f, frameTimer, WHITE_TEXT);
drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 0.0f, 0, frameTimer, WHITE_TEXT);
}
_overlays.render2D();
@ -3027,6 +3033,7 @@ void Application::resetSensors() {
_mouseX = _glWidget->width() / 2;
_mouseY = _glWidget->height() / 2;
_faceplus.reset();
_faceshift.reset();
_visage.reset();
@ -3576,3 +3583,38 @@ void Application::takeSnapshot() {
Snapshot::saveSnapshot(_glWidget, _myAvatar);
}
void Application::urlGoTo(int argc, const char * constArgv[]) {
//Gets the url (hifi://domain/destination/orientation)
QString customUrl = getCmdOption(argc, constArgv, "-url");
if (customUrl.startsWith("hifi://")) {
QStringList urlParts = customUrl.remove(0, CUSTOM_URL_SCHEME.length() + 2).split('/', QString::SkipEmptyParts);
if (urlParts.count() > 1) {
// if url has 2 or more parts, the first one is domain name
QString domain = urlParts[0];
// second part is either a destination coordinate or
// a place name
QString destination = urlParts[1];
// any third part is an avatar orientation.
QString orientation = urlParts.count() > 2 ? urlParts[2] : QString();
Menu::goToDomain(domain);
// goto either @user, #place, or x-xx,y-yy,z-zz
// style co-ordinate.
Menu::goTo(destination);
if (!orientation.isEmpty()) {
// location orientation
Menu::goToOrientation(orientation);
}
} else if (urlParts.count() == 1) {
// location coordinates or place name
QString destination = urlParts[0];
Menu::goTo(destination);
}
}
}

View file

@ -129,6 +129,7 @@ public:
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
void urlGoTo(int argc, const char * constArgv[]);
void keyPressEvent(QKeyEvent* event);
void keyReleaseEvent(QKeyEvent* event);

View file

@ -45,7 +45,7 @@ Camera::Camera() :
_idealPosition(0.0f, 0.0f, 0.0f),
_targetPosition(0.0f, 0.0f, 0.0f),
_fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES),
_aspectRatio(16.f/9.f),
_aspectRatio(16.0f/9.0f),
_nearClip(0.08f), // default
_farClip(50.0f * TREE_SCALE), // default
_upShift(0.0f),
@ -94,8 +94,8 @@ void Camera::updateFollowMode(float deltaTime) {
// derive t from tightness
float t = _tightness * _modeShift * deltaTime;
if (t > 1.0) {
t = 1.0;
if (t > 1.0f) {
t = 1.0f;
}
// handle keepLookingAt

View file

@ -317,6 +317,7 @@ Menu::Menu() :
appInstance->getVisage(), SLOT(updateEnabled()));
#endif
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::GlowWhenSpeaking, 0, true);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false);
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");

View file

@ -136,10 +136,10 @@ public:
void removeAction(QMenu* menu, const QString& actionName);
bool goToDestination(QString destination);
void goToOrientation(QString orientation);
void goToDomain(const QString newDomain);
void goTo(QString destination);
bool static goToDestination(QString destination);
void static goToOrientation(QString orientation);
void static goToDomain(const QString newDomain);
void static goTo(QString destination);
public slots:
@ -307,6 +307,7 @@ namespace MenuOption {
const QString Fullscreen = "Fullscreen";
const QString FullscreenMirror = "Fullscreen Mirror";
const QString GlowMode = "Cycle Glow Mode";
const QString GlowWhenSpeaking = "Glow When Speaking";
const QString GoHome = "Go Home";
const QString GoTo = "Go To...";
const QString GoToDomain = "Go To Domain...";

View file

@ -34,6 +34,7 @@ static const QString TEXDIR_FIELD = "texdir";
static const QString LOD_FIELD = "lod";
static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
static const QString DATA_SERVER_URL = "https://data-web.highfidelity.io";
static const QString MODEL_URL = "/api/v1/models";
static const QString SETTING_NAME = "LastModelUploadLocation";
@ -201,14 +202,14 @@ void ModelUploader::send() {
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "uploadSuccess";
callbackParams.jsonCallbackMethod = "checkJSON";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "uploadFailed";
callbackParams.updateReciever = this;
callbackParams.updateSlot = SLOT(uploadUpdate(qint64, qint64));
AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, callbackParams, QByteArray(), _dataMultiPart);
_dataMultiPart = NULL;
AccountManager::getInstance().authenticatedRequest(MODEL_URL + "/" + QFileInfo(_url).baseName(),
QNetworkAccessManager::GetOperation,
callbackParams);
qDebug() << "Sending model...";
_progressDialog = new QDialog();
_progressBar = new QProgressBar(_progressDialog);
@ -226,6 +227,61 @@ void ModelUploader::send() {
_progressBar = NULL;
}
void ModelUploader::checkJSON(const QJsonObject& jsonResponse) {
if (jsonResponse.contains("status") && jsonResponse.value("status").toString() == "success") {
qDebug() << "status : success";
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "uploadSuccess";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "uploadFailed";
callbackParams.updateReciever = this;
callbackParams.updateSlot = SLOT(uploadUpdate(qint64, qint64));
if (jsonResponse.contains("exists") && jsonResponse.value("exists").toBool()) {
qDebug() << "exists : true";
if (jsonResponse.contains("can_update") && jsonResponse.value("can_update").toBool()) {
qDebug() << "can_update : true";
AccountManager::getInstance().authenticatedRequest(MODEL_URL + "/" + QFileInfo(_url).baseName(),
QNetworkAccessManager::PutOperation,
callbackParams,
QByteArray(),
_dataMultiPart);
_dataMultiPart = NULL;
} else {
qDebug() << "can_update : false";
if (_progressDialog) {
_progressDialog->reject();
}
QMessageBox::warning(NULL,
QString("ModelUploader::checkJSON()"),
QString("This model already exist and is own by someone else."),
QMessageBox::Ok);
deleteLater();
}
} else {
qDebug() << "exists : false";
AccountManager::getInstance().authenticatedRequest(MODEL_URL,
QNetworkAccessManager::PostOperation,
callbackParams,
QByteArray(),
_dataMultiPart);
_dataMultiPart = NULL;
}
} else {
qDebug() << "status : failed";
if (_progressDialog) {
_progressDialog->reject();
}
QMessageBox::warning(NULL,
QString("ModelUploader::checkJSON()"),
QString("Something went wrong with the data-server."),
QMessageBox::Ok);
deleteLater();
}
}
void ModelUploader::uploadUpdate(qint64 bytesSent, qint64 bytesTotal) {
if (_progressDialog) {
_progressBar->setRange(0, bytesTotal);
@ -249,11 +305,11 @@ void ModelUploader::uploadFailed(QNetworkReply::NetworkError errorCode, const QS
if (_progressDialog) {
_progressDialog->reject();
}
qDebug() << "Model upload failed (" << errorCode << "): " << errorString;
QMessageBox::warning(NULL,
QString("ModelUploader::uploadFailed()"),
QString("There was a problem with your upload, please try again later."),
QMessageBox::Ok);
qDebug() << "Model upload failed (" << errorCode << "): " << errorString;
deleteLater();
}

View file

@ -30,6 +30,7 @@ public slots:
void send();
private slots:
void checkJSON(const QJsonObject& jsonResponse);
void uploadUpdate(qint64 bytesSent, qint64 bytesTotal);
void uploadSuccess(const QJsonObject& jsonResponse);
void uploadFailed(QNetworkReply::NetworkError errorCode, const QString& errorString);

View file

@ -211,9 +211,14 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
const float GLOW_DISTANCE = 20.0f;
const float GLOW_MAX_LOUDNESS = 2500.0f;
const float MAX_GLOW = 0.5f;
const float GLOW_FROM_AVERAGE_LOUDNESS = ((this == Application::getInstance()->getAvatar())
float GLOW_FROM_AVERAGE_LOUDNESS = ((this == Application::getInstance()->getAvatar())
? 0.0f
: MAX_GLOW * getHeadData()->getAudioLoudness() / GLOW_MAX_LOUDNESS);
if (!Menu::getInstance()->isOptionChecked(MenuOption::GlowWhenSpeaking)) {
GLOW_FROM_AVERAGE_LOUDNESS = 0.0f;
}
Glower glower(_moving && distanceToTarget > GLOW_DISTANCE && renderMode == NORMAL_RENDER_MODE
? 1.0f
: GLOW_FROM_AVERAGE_LOUDNESS);

View file

@ -41,8 +41,15 @@ void Faceplus::init() {
updateEnabled();
}
void Faceplus::setState(const glm::quat& headRotation, float estimatedEyePitch, float estimatedEyeYaw,
const QVector<float>& blendshapeCoefficients) {
void Faceplus::reset() {
if (_enabled) {
QMetaObject::invokeMethod(_reader, "reset");
}
}
void Faceplus::setState(const glm::vec3& headTranslation, const glm::quat& headRotation,
float estimatedEyePitch, float estimatedEyeYaw, const QVector<float>& blendshapeCoefficients) {
_headTranslation = headTranslation;
_headRotation = headRotation;
_estimatedEyePitch = estimatedEyePitch;
_estimatedEyeYaw = estimatedEyeYaw;
@ -150,7 +157,7 @@ FaceplusReader::~FaceplusReader() {
void FaceplusReader::init() {
#ifdef HAVE_FACEPLUS
if (!faceplus_init("VGA")) {
if (!faceplus_init("hHD")) {
qDebug() << "Failed to initialized Faceplus.";
return;
}
@ -191,6 +198,7 @@ void FaceplusReader::init() {
}
}
_blendshapeCoefficients.resize(maxIndex + 1);
_referenceInitialized = false;
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
#endif
@ -203,10 +211,24 @@ void FaceplusReader::shutdown() {
void FaceplusReader::update() {
#ifdef HAVE_FACEPLUS
if (!(faceplus_synchronous_track() && faceplus_current_output_vector(_outputVector.data()))) {
float x, y, rotation, scale;
if (!(faceplus_synchronous_track() && faceplus_current_face_location(&x, &y, &rotation, &scale) && !glm::isnan(x) &&
faceplus_current_output_vector(_outputVector.data()))) {
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
return;
}
if (!_referenceInitialized) {
_referenceX = x;
_referenceY = y;
_referenceScale = scale;
_referenceInitialized = true;
}
const float TRANSLATION_SCALE = 10.0f;
const float REFERENCE_DISTANCE = 10.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]),
_outputVector.at(_headRotationIndices[1]), -_outputVector.at(_headRotationIndices[2]))));
float estimatedEyePitch = (_outputVector.at(_leftEyeRotationIndices[0]) +
@ -222,10 +244,16 @@ void FaceplusReader::update() {
}
}
QMetaObject::invokeMethod(Application::getInstance()->getFaceplus(), "setState", Q_ARG(const glm::quat&, headRotation),
Q_ARG(float, estimatedEyePitch), Q_ARG(float, estimatedEyeYaw), Q_ARG(const QVector<float>&, _blendshapeCoefficients));
QMetaObject::invokeMethod(Application::getInstance()->getFaceplus(), "setState", Q_ARG(const glm::vec3&, headTranslation),
Q_ARG(const glm::quat&, headRotation), Q_ARG(float, estimatedEyePitch), Q_ARG(float, estimatedEyeYaw),
Q_ARG(const QVector<float>&, _blendshapeCoefficients));
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
#endif
}
void FaceplusReader::reset() {
#ifdef HAVE_FACEPLUS
_referenceInitialized = false;
#endif
}

View file

@ -30,11 +30,12 @@ public:
virtual ~Faceplus();
void init();
void reset();
bool isActive() const { return _active; }
Q_INVOKABLE void setState(const glm::quat& headRotation, float estimatedEyePitch, float estimatedEyeYaw,
const QVector<float>& blendshapeCoefficients);
Q_INVOKABLE void setState(const glm::vec3& headTranslation, const glm::quat& headRotation,
float estimatedEyePitch, float estimatedEyeYaw, const QVector<float>& blendshapeCoefficients);
public slots:
@ -63,6 +64,7 @@ public:
Q_INVOKABLE void init();
Q_INVOKABLE void shutdown();
Q_INVOKABLE void update();
Q_INVOKABLE void reset();
private:
@ -72,6 +74,10 @@ private:
int _headRotationIndices[3];
int _leftEyeRotationIndices[2];
int _rightEyeRotationIndices[2];
float _referenceX;
float _referenceY;
float _referenceScale;
bool _referenceInitialized;
QVector<float> _blendshapeCoefficients;
#endif
};

View file

@ -60,11 +60,11 @@ Model::SkinLocations Model::_skinNormalMapLocations;
Model::SkinLocations Model::_skinShadowLocations;
void Model::setScale(const glm::vec3& scale) {
glm::vec3 deltaScale = _scale - scale;
float scaleLength = glm::length(_scale);
float relativeDeltaScale = glm::length(_scale - scale) / scaleLength;
// decreased epsilon because this wasn't handling scale changes of 0.01
const float SMALLER_EPSILON = EPSILON * 0.0001f;
if (glm::length2(deltaScale) > SMALLER_EPSILON) {
const float ONE_PERCENT = 0.01f;
if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) {
_scale = scale;
rebuildShapes();
}
@ -469,19 +469,55 @@ void Model::clearShapes() {
void Model::rebuildShapes() {
clearShapes();
if (_jointStates.isEmpty()) {
if (!_geometry) {
return;
}
// make sure all the joints are updated correctly before we try to create their shapes
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (geometry.joints.isEmpty()) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
int numJoints = geometry.joints.size();
QVector<glm::mat4> transforms;
transforms.fill(glm::mat4(), numJoints);
QVector<glm::quat> combinedRotations;
combinedRotations.fill(glm::quat(), numJoints);
QVector<bool> shapeIsSet;
shapeIsSet.fill(false, numJoints);
int rootIndex = 0;
float uniformScale = extractUniformScale(_scale);
glm::quat inverseRotation = glm::inverse(_rotation);
glm::vec3 rootPosition(0.f);
int numShapesSet = 0;
int lastNumShapesSet = -1;
while (numShapesSet < numJoints && numShapesSet != lastNumShapesSet) {
lastNumShapesSet = numShapesSet;
for (int i = 0; i < numJoints; ++i) {
if (shapeIsSet[i]) {
continue;
}
const FBXJoint& joint = geometry.joints[i];
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
rootIndex = i;
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * uniformScale * glm::translate(_offset);
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = baseTransform * geometry.offset * glm::translate(joint.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
combinedRotations[i] = _rotation * combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
} else if (shapeIsSet[parentIndex]) {
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
combinedRotations[i] = combinedRotations[parentIndex] * combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
}
}
}
// joint shapes
Extents totalExtents;
@ -489,48 +525,70 @@ void Model::rebuildShapes() {
for (int i = 0; i < _jointStates.size(); i++) {
const FBXJoint& joint = geometry.joints[i];
glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i].combinedRotation * joint.shapePosition);
glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation;
glm::vec3 worldPosition = extractTranslation(transforms[i]);
Extents shapeExtents;
shapeExtents.reset();
if (joint.parentIndex == -1) {
rootPosition = worldPosition;
}
float radius = uniformScale * joint.boneRadius;
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
if (joint.shapeType == Shape::CAPSULE_SHAPE && halfHeight > EPSILON) {
Shape::Type type = joint.shapeType;
if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) {
// this capsule is effectively a sphere
type = Shape::SPHERE_SHAPE;
}
if (type == Shape::CAPSULE_SHAPE) {
CapsuleShape* capsule = new CapsuleShape(radius, halfHeight);
capsule->setPosition(worldPosition);
capsule->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation);
capsule->setRotation(combinedRotations[i] * joint.shapeRotation);
_jointShapes.push_back(capsule);
glm::vec3 endPoint;
capsule->getEndPoint(endPoint);
glm::vec3 startPoint;
capsule->getStartPoint(startPoint);
glm::vec3 axis = (halfHeight + radius) * glm::normalize(endPoint - startPoint);
// add some points that bound a sphere at the center of the capsule
glm::vec3 axis = glm::vec3(radius);
shapeExtents.addPoint(worldPosition + axis);
shapeExtents.addPoint(worldPosition - axis);
} else {
// add the two furthest surface points of the capsule
axis = (halfHeight + radius) * glm::normalize(endPoint - startPoint);
shapeExtents.addPoint(worldPosition + axis);
shapeExtents.addPoint(worldPosition - axis);
totalExtents.addExtents(shapeExtents);
} else if (type == Shape::SPHERE_SHAPE) {
SphereShape* sphere = new SphereShape(radius, worldPosition);
_jointShapes.push_back(sphere);
glm::vec3 axis = glm::vec3(radius);
shapeExtents.addPoint(worldPosition + axis);
shapeExtents.addPoint(worldPosition - axis);
}
totalExtents.addExtents(shapeExtents);
} else {
// this shape type is not handled and the joint shouldn't collide,
// however we must have a shape for each joint,
// so we make a bogus sphere with zero radius.
// TODO: implement collision groups for more control over what collides with what
SphereShape* sphere = new SphereShape(0.f, worldPosition);
_jointShapes.push_back(sphere);
}
}
// bounding shape
// NOTE: we assume that the longest side of totalExtents is the yAxis
glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum;
float capsuleRadius = 0.25f * (diagonal.x + diagonal.z); // half the average of x and z
// the radius is half the RMS of the X and Z sides:
float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
_boundingShape.setRadius(capsuleRadius);
_boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius);
glm::quat inverseRotation = glm::inverse(_rotation);
glm::vec3 rootPosition = extractTranslation(transforms[rootIndex]);
_boundingShapeLocalOffset = inverseRotation * (0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition);
_boundingShape.setPosition(_translation - _rotation * _boundingShapeLocalOffset);
_boundingShape.setRotation(_rotation);
}
void Model::updateShapePositions() {
@ -557,6 +615,7 @@ void Model::updateShapePositions() {
_boundingRadius = sqrtf(_boundingRadius);
_shapesAreDirty = false;
_boundingShape.setPosition(rootPosition + _rotation * _boundingShapeLocalOffset);
_boundingShape.setRotation(_rotation);
}
}

View file

@ -78,7 +78,7 @@ void ClipboardScriptingInterface::exportVoxel(float x, float y, float z, float s
}
bool ClipboardScriptingInterface::importVoxels() {
qDebug() << "[DEBUG] Importing ... ";
qDebug() << "Importing ... ";
QEventLoop loop;
connect(Application::getInstance(), SIGNAL(importDone()), &loop, SLOT(quit()));
emit readyToImport();

View file

@ -171,6 +171,9 @@ void Stats::display(
unsigned int backgroundColor = 0x33333399;
int verticalOffset = 0, lines = 0;
float scale = 0.10f;
float rotation = 0.0f;
int font = 2;
QLocale locale(QLocale::English);
std::stringstream voxelStats;
@ -198,11 +201,11 @@ void Stats::display(
sprintf(framesPerSecond, "Framerate: %3.0f FPS", fps);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, serverNodes, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, serverNodes, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarNodes, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, framesPerSecond, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond, color);
if (_expanded) {
char packetsPerSecondString[30];
@ -211,9 +214,9 @@ void Stats::display(
sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)bytesPerSecond * 8.f / 1000000.f);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, packetsPerSecondString, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, averageMegabitsPerSecond, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond, color);
}
verticalOffset = 0;
@ -258,7 +261,7 @@ void Stats::display(
"Buffer msecs %.1f",
(float) (audio->getNetworkBufferLengthSamplesPerChannel() + (float) audio->getJitterBufferSamples()) /
(float) audio->getNetworkSampleRate() * 1000.f);
drawText(30, glWidget->height() - 22, 0.10f, 0.f, 2.f, audioJitter, color);
drawText(30, glWidget->height() - 22, scale, rotation, font, audioJitter, color);
char audioPing[30];
@ -271,18 +274,18 @@ void Stats::display(
sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, audioPing, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioPing, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPing, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPing, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelAvgPing, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelAvgPing, color);
if (_expanded) {
char voxelMaxPing[30];
sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelMaxPing, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing, color);
}
verticalOffset = 0;
@ -306,11 +309,11 @@ void Stats::display(
char avatarMixerStats[200];
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPosition, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPosition, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarVelocity, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarVelocity, color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarBodyYaw, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarBodyYaw, color);
if (_expanded) {
SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AvatarMixer);
@ -323,7 +326,7 @@ void Stats::display(
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarMixerStats, color);
stringstream downloads;
downloads << "Downloads: ";
@ -333,7 +336,7 @@ void Stats::display(
downloads << "(" << ResourceCache::getPendingRequestCount() << " pending)";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, downloads.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color);
}
verticalOffset = 0;
@ -354,7 +357,7 @@ void Stats::display(
voxelStats.str("");
voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
voxelStats.str("");
voxelStats <<
@ -364,14 +367,14 @@ void Stats::display(
voxelStats << " / GPU: " << voxels->getVoxelMemoryUsageGPU() / 1000000.f << "MB";
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
// Voxel Rendering
voxelStats.str("");
voxelStats.precision(4);
voxelStats << "Voxel Rendering Slots Max: " << voxels->getMaxVoxels() / 1000.f << "K";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
}
voxelStats.str("");
@ -379,7 +382,7 @@ void Stats::display(
voxelStats << "Drawn: " << voxels->getVoxelsWritten() / 1000.f << "K " <<
"Abandoned: " << voxels->getAbandonedVoxels() / 1000.f << "K ";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
// iterate all the current voxel stats, and list their sending modes, and total voxel counts
std::stringstream sendingMode("");
@ -424,7 +427,7 @@ void Stats::display(
sendingMode << " <SCENE STABLE>";
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)sendingMode.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)sendingMode.str().c_str(), color);
}
// Incoming packets
@ -435,7 +438,7 @@ void Stats::display(
voxelStats << "Voxel Packets to Process: " << qPrintable(packetsString)
<< " [Recent Max: " << qPrintable(maxString) << "]";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
}
if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) {
@ -458,7 +461,7 @@ void Stats::display(
voxelStats.str("");
voxelStats << "Server voxels: " << qPrintable(serversTotalString);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
if (_expanded) {
QString serversInternalString = locale.toString((uint)totalInternal);
@ -469,7 +472,7 @@ void Stats::display(
"Internal: " << qPrintable(serversInternalString) << " " <<
"Leaves: " << qPrintable(serversLeavesString) << "";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
}
unsigned long localTotal = VoxelTreeElement::getNodeCount();
@ -479,7 +482,7 @@ void Stats::display(
voxelStats.str("");
voxelStats << "Local voxels: " << qPrintable(localTotalString);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
if (_expanded) {
unsigned long localInternal = VoxelTreeElement::getInternalNodeCount();
@ -492,7 +495,7 @@ void Stats::display(
"Internal: " << qPrintable(localInternalString) << " " <<
"Leaves: " << qPrintable(localLeavesString) << "";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
}
// LOD Details
@ -501,7 +504,7 @@ void Stats::display(
QString displayLODDetails = Menu::getInstance()->getLODFeedbackText();
voxelStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed());
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)voxelStats.str().c_str(), color);
}
@ -526,7 +529,7 @@ void Stats::display(
);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
float preDelay = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingPreDelay) ?
audioReflector->getPreDelay() : 0.0f;
@ -539,7 +542,8 @@ void Stats::display(
audioReflector->getSoundMsPerMeter());
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
bool distanceAttenuationDisabled = Menu::getInstance()->isOptionChecked(
MenuOption::AudioSpatialProcessingDontDistanceAttenuate);
@ -556,7 +560,7 @@ void Stats::display(
audioReflector->getDistanceAttenuationScalingFactor());
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
sprintf(reflectionsStatus, "Local Audio: %s Attenuation: %5.3f",
(Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingProcessLocalAudio)
@ -564,7 +568,7 @@ void Stats::display(
audioReflector->getLocalAudioAttenuationFactor());
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
bool diffusionEnabled = Menu::getInstance()->isOptionChecked(MenuOption::AudioSpatialProcessingWithDiffusions);
int fanout = diffusionEnabled ? audioReflector->getDiffusionFanout() : 0;
@ -573,7 +577,7 @@ void Stats::display(
(diffusionEnabled ? "yes" : "no"), fanout, diffusionPaths);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
const float AS_PERCENT = 100.0f;
float reflectiveRatio = audioReflector->getReflectiveRatio() * AS_PERCENT;
@ -583,7 +587,7 @@ void Stats::display(
reflectiveRatio, diffusionRatio, absorptionRatio);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
sprintf(reflectionsStatus, "Comb Filter Window: %5.3f ms, Allowed: %d, Suppressed: %d",
audioReflector->getCombFilterWindow(),
@ -591,7 +595,7 @@ void Stats::display(
audioReflector->getEchoesSuppressed());
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color);
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reflectionsStatus, color);
sprintf(reflectionsStatus, "Wet/Dry Mix: Original: %5.3f Echoes: %5.3f",
audioReflector->getOriginalSourceAttenuation(),

View file

@ -40,6 +40,7 @@ void Cube3DOverlay::render() {
if (_isSolid) {
glutSolidCube(_size);
} else {
glLineWidth(_lineWidth);
glutWireCube(_size);
}
glPopMatrix();

View file

@ -67,20 +67,24 @@ Sound::Sound(float volume, float frequency, float duration, float decay, QObject
}
Sound::Sound(const QUrl& sampleURL, QObject* parent) :
QObject(parent)
QObject(parent),
_hasDownloaded(false)
{
// assume we have a QApplication or QCoreApplication instance and use the
// QNetworkAccess manager to grab the raw audio file at the given URL
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
qDebug() << "Requesting audio file" << sampleURL.toDisplayString();
manager->get(QNetworkRequest(sampleURL));
QNetworkReply* soundDownload = manager->get(QNetworkRequest(sampleURL));
connect(soundDownload, &QNetworkReply::finished, this, &Sound::replyFinished);
connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError)));
}
void Sound::replyFinished(QNetworkReply* reply) {
void Sound::replyFinished() {
QNetworkReply* reply = reinterpret_cast<QNetworkReply*>(sender());
// replace our byte array with the downloaded data
QByteArray rawAudioByteArray = reply->readAll();
@ -108,6 +112,13 @@ void Sound::replyFinished(QNetworkReply* reply) {
} else {
qDebug() << "Network reply without 'Content-Type'.";
}
_hasDownloaded = true;
}
void Sound::replyError(QNetworkReply::NetworkError code) {
QNetworkReply* reply = reinterpret_cast<QNetworkReply*>(sender());
qDebug() << "Error downloading sound file at" << reply->url().toString() << "-" << reply->errorString();
}
void Sound::downSample(const QByteArray& rawAudioByteArray) {

View file

@ -13,25 +13,30 @@
#define hifi_Sound_h
#include <QtCore/QObject>
class QNetworkReply;
#include <QtNetwork/QNetworkReply>
class Sound : public QObject {
Q_OBJECT
Q_PROPERTY(bool downloaded READ hasDownloaded)
public:
Sound(const QUrl& sampleURL, QObject* parent = NULL);
Sound(float volume, float frequency, float duration, float decay, QObject* parent = NULL);
bool hasDownloaded() const { return _hasDownloaded; }
const QByteArray& getByteArray() { return _byteArray; }
private:
QByteArray _byteArray;
bool _hasDownloaded;
void downSample(const QByteArray& rawAudioByteArray);
void interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
private slots:
void replyFinished(QNetworkReply* reply);
void replyFinished();
void replyError(QNetworkReply::NetworkError code);
};
#endif // hifi_Sound_h

View file

@ -97,13 +97,14 @@ class AvatarData : public QObject {
Q_PROPERTY(float audioLoudness READ getAudioLoudness WRITE setAudioLoudness)
Q_PROPERTY(float audioAverageLoudness READ getAudioAverageLoudness WRITE setAudioAverageLoudness)
Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName)
Q_PROPERTY(QString faceModelURL READ getFaceModelURLFromScript WRITE setFaceModelURLFromScript)
Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript)
Q_PROPERTY(QString billboardURL READ getBillboardURL WRITE setBillboardFromURL)
Q_PROPERTY(QStringList jointNames READ getJointNames)
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID);
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
public:
AvatarData();
virtual ~AvatarData();

View file

@ -1624,7 +1624,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
JointShapeInfo& jointShapeInfo = jointShapeInfos[jointIndex];
jointShapeInfo.boneBegin = rotateMeshToJoint * (radiusScale * (boneBegin - boneEnd));
float totalWeight = 0.0f;
for (int j = 0; j < cluster.indices.size(); j++) {
@ -1686,7 +1685,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
}
}
float radiusScale = extractUniformScale(joint.transform * firstFBXCluster.inverseBindMatrix);
jointShapeInfo.boneBegin = rotateMeshToJoint * (radiusScale * (boneBegin - boneEnd));
glm::vec3 averageVertex(0.f);
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
@ -1722,6 +1720,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
FBXJoint& joint = geometry.joints[i];
JointShapeInfo& jointShapeInfo = jointShapeInfos[i];
if (joint.parentIndex == -1) {
jointShapeInfo.boneBegin = glm::vec3(0.0f);
} else {
const FBXJoint& parentJoint = geometry.joints[joint.parentIndex];
glm::quat inverseRotation = glm::inverse(extractRotation(joint.transform));
jointShapeInfo.boneBegin = inverseRotation * (extractTranslation(parentJoint.transform) - extractTranslation(joint.transform));
}
// we use a capsule if the joint ANY mesh vertices successfully projected onto the bone
// AND its boneRadius is not too close to zero
bool collideLikeCapsule = jointShapeInfo.numProjectedVertices > 0
@ -1733,12 +1739,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
joint.shapeType = Shape::CAPSULE_SHAPE;
} else {
// collide the joint like a sphere
joint.shapeType = Shape::SPHERE_SHAPE;
if (jointShapeInfo.numVertices > 0) {
jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices;
joint.shapePosition = jointShapeInfo.averageVertex;
} else {
joint.shapePosition = glm::vec3(0.f);
joint.shapeType = Shape::SPHERE_SHAPE;
}
if (jointShapeInfo.numProjectedVertices == 0
&& jointShapeInfo.numVertices > 0) {
@ -1747,6 +1753,15 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
jointShapeInfo.averageRadius /= (float)jointShapeInfo.numVertices;
joint.boneRadius = jointShapeInfo.averageRadius;
}
float distanceFromEnd = glm::length(joint.shapePosition);
float distanceFromBegin = glm::distance(joint.shapePosition, jointShapeInfo.boneBegin);
if (distanceFromEnd > joint.distanceToParent && distanceFromBegin > joint.distanceToParent) {
// The shape is further from both joint endpoints than the endpoints are from each other
// which probably means the model has a bad transform somewhere. We disable this shape
// by setting its type to UNKNOWN_SHAPE.
joint.shapeType = Shape::UNKNOWN_SHAPE;
}
}
}
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());

View file

@ -18,6 +18,8 @@
#include <QVariant>
#include <QVector>
#include <Shape.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -91,7 +93,7 @@ public:
QString name;
glm::vec3 shapePosition; // in joint frame
glm::quat shapeRotation; // in joint frame
int shapeType;
Shape::Type shapeType;
};

View file

@ -37,6 +37,9 @@ public:
public slots:
/// provide the world scale
const int getTreeScale() const { return TREE_SCALE; }
/// checks the local voxel tree for a voxel at the specified location and scale
/// \param x the x-coordinate of the voxel (in meter units)
/// \param y the y-coordinate of the voxel (in meter units)