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

This commit is contained in:
Atlante45 2014-02-20 16:57:31 -08:00
commit d06f526bf4
11 changed files with 934 additions and 135 deletions

View file

@ -37,6 +37,9 @@ const char METAVOXEL_SERVER_LOGGING_NAME[] = "metavoxel-server";
void MetavoxelServer::run() {
commonInit(METAVOXEL_SERVER_LOGGING_NAME, NodeType::MetavoxelServer);
NodeList* nodeList = NodeList::getInstance();
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
_lastSend = QDateTime::currentMSecsSinceEpoch();
_sendTimer.start(SEND_INTERVAL);
}

View file

@ -0,0 +1,151 @@
//
// clipboardExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 1/28/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// This is an example script that demonstrates use of the Clipboard class
//
//
var selectedVoxel = { x: 0, y: 0, z: 0, s: 0 };
var selectedSize = 4;
function printKeyEvent(eventName, event) {
print(eventName);
print(" event.key=" + event.key);
print(" event.text=" + event.text);
print(" event.isShifted=" + event.isShifted);
print(" event.isControl=" + event.isControl);
print(" event.isMeta=" + event.isMeta);
print(" event.isAlt=" + event.isAlt);
print(" event.isKeypad=" + event.isKeypad);
}
function keyPressEvent(event) {
var debug = false;
if (debug) {
printKeyEvent("keyPressEvent", event);
}
}
function keyReleaseEvent(event) {
var debug = false;
if (debug) {
printKeyEvent("keyReleaseEvent", event);
}
// Note: this sample uses Alt+ as the key codes for these clipboard items
if ((event.key == 199 || event.key == 67 || event.text == "C" || event.text == "c") && event.isAlt) {
print("the Alt+C key was pressed");
Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if ((event.key == 8776 || event.key == 88 || event.text == "X" || event.text == "x") && event.isAlt) {
print("the Alt+X key was pressed");
Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if ((event.key == 8730 || event.key == 86 || event.text == "V" || event.text == "v") && event.isAlt) {
print("the Alt+V key was pressed");
Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if (event.text == "DELETE" || event.text == "BACKSPACE") {
print("the DELETE/BACKSPACE key was pressed");
Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if ((event.text == "E" || event.text == "e") && event.isMeta) {
print("the Ctl+E key was pressed");
Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if ((event.text == "I" || event.text == "i") && event.isMeta) {
print("the Ctl+I key was pressed");
Clipboard.importVoxels();
}
if ((event.key == 78 || event.text == "N" || event.text == "n") && event.isMeta) {
print("the Ctl+N key was pressed, nudging to left 1 meter");
Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 });
}
}
// Map keyPress and mouse move events to our callbacks
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
var selectCube = Overlays.addOverlay("cube", {
position: { x: 0, y: 0, z: 0},
size: selectedSize,
color: { red: 255, green: 255, blue: 0},
alpha: 1,
solid: false,
visible: false,
lineWidth: 4
});
function mouseMoveEvent(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
var debug = false;
if (debug) {
print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y);
print("called Camera.computePickRay()");
print("computePickRay origin=" + pickRay.origin.x + ", " + pickRay.origin.y + ", " + pickRay.origin.z);
print("computePickRay direction=" + pickRay.direction.x + ", " + pickRay.direction.y + ", " + pickRay.direction.z);
}
var intersection = Voxels.findRayIntersection(pickRay);
if (intersection.intersects) {
if (debug) {
print("intersection voxel.red/green/blue=" + intersection.voxel.red + ", "
+ intersection.voxel.green + ", " + intersection.voxel.blue);
print("intersection voxel.x/y/z/s=" + intersection.voxel.x + ", "
+ intersection.voxel.y + ", " + intersection.voxel.z+ ": " + intersection.voxel.s);
print("intersection face=" + intersection.face);
print("intersection distance=" + intersection.distance);
print("intersection intersection.x/y/z=" + intersection.intersection.x + ", "
+ intersection.intersection.y + ", " + intersection.intersection.z);
}
var x = Math.floor(intersection.voxel.x / selectedSize) * selectedSize;
var y = Math.floor(intersection.voxel.y / selectedSize) * selectedSize;
var z = Math.floor(intersection.voxel.z / selectedSize) * selectedSize;
selectedVoxel = { x: x, y: y, z: z, s: selectedSize };
Overlays.editOverlay(selectCube, { position: selectedVoxel, size: selectedSize, visible: true } );
} else {
Overlays.editOverlay(selectCube, { visible: false } );
selectedVoxel = { x: 0, y: 0, z: 0, s: 0 };
}
}
Controller.mouseMoveEvent.connect(mouseMoveEvent);
function wheelEvent(event) {
var debug = false;
if (debug) {
print("wheelEvent");
print(" event.x,y=" + event.x + ", " + event.y);
print(" event.delta=" + event.delta);
print(" event.orientation=" + event.orientation);
print(" event.isLeftButton=" + event.isLeftButton);
print(" event.isRightButton=" + event.isRightButton);
print(" event.isMiddleButton=" + event.isMiddleButton);
print(" event.isShifted=" + event.isShifted);
print(" event.isControl=" + event.isControl);
print(" event.isMeta=" + event.isMeta);
print(" event.isAlt=" + event.isAlt);
}
}
Controller.wheelEvent.connect(wheelEvent);
function scriptEnding() {
Overlays.deleteOverlay(selectCube);
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -72,6 +72,10 @@ audioOptions.volume = 0.5;
var editToolsOn = false; // 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;
var voxelPreview = Overlays.addOverlay("cube", {
position: { x: 0, y: 0, z: 0},
size: 1,
@ -81,14 +85,54 @@ var voxelPreview = Overlays.addOverlay("cube", {
visible: false,
lineWidth: 4
});
var linePreviewTop = Overlays.addOverlay("line3d", {
position: { x: 0, y: 0, z: 0},
end: { x: 0, y: 0, z: 0},
color: { red: 255, green: 255, blue: 255},
alpha: 1,
visible: false,
lineWidth: 1
});
var linePreviewBottom = Overlays.addOverlay("line3d", {
position: { x: 0, y: 0, z: 0},
end: { x: 0, y: 0, z: 0},
color: { red: 255, green: 255, blue: 255},
alpha: 1,
visible: false,
lineWidth: 1
});
var linePreviewLeft = Overlays.addOverlay("line3d", {
position: { x: 0, y: 0, z: 0},
end: { x: 0, y: 0, z: 0},
color: { red: 255, green: 255, blue: 255},
alpha: 1,
visible: false,
lineWidth: 1
});
var linePreviewRight = Overlays.addOverlay("line3d", {
position: { x: 0, y: 0, z: 0},
end: { x: 0, y: 0, z: 0},
color: { red: 255, green: 255, blue: 255},
alpha: 1,
visible: false,
lineWidth: 1
});
// these will be used below
var sliderWidth = 158;
var sliderHeight = 35;
// These will be our "overlay IDs"
var swatches = new Array();
var swatchHeight = 54;
var swatchWidth = 31;
var swatchesWidth = swatchWidth * numColors;
var swatchesX = (windowDimensions.x - swatchesWidth) / 2;
var swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
var swatchesY = windowDimensions.y - swatchHeight;
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
@ -115,6 +159,143 @@ for (s = 0; s < numColors; s++) {
}
// These will be our tool palette overlays
var numberOfTools = 5;
var toolHeight = 40;
var toolWidth = 62;
var toolsHeight = toolHeight * numberOfTools;
var toolsX = 0;
var toolsY = (windowDimensions.y - toolsHeight) / 2;
var addToolAt = 0;
var deleteToolAt = 1;
var recolorToolAt = 2;
var eyedropperToolAt = 3;
var selectToolAt = 4;
var toolSelectedColor = { red: 255, green: 255, blue: 255 };
var notSelectedColor = { red: 128, green: 128, blue: 128 };
var addTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight * addToolAt, width: toolWidth, height: toolHeight },
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
color: toolSelectedColor,
visible: false,
alpha: 0.9
});
var deleteTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight * deleteToolAt, width: toolWidth, height: toolHeight },
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
color: toolSelectedColor,
visible: false,
alpha: 0.9
});
var recolorTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight * recolorToolAt, width: toolWidth, height: toolHeight },
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
color: toolSelectedColor,
visible: false,
alpha: 0.9
});
var eyedropperTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight * eyedropperToolAt, width: toolWidth, height: toolHeight },
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
color: toolSelectedColor,
visible: false,
alpha: 0.9
});
var selectTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight * selectToolAt, width: toolWidth, height: toolHeight },
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
color: toolSelectedColor,
visible: false,
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 sliderX = swatchesX + swatchesWidth;
var sliderY = windowDimensions.y - sliderHeight;
var slider = Overlays.addOverlay("image", {
// alternate form of expressing bounds
bounds: { x: sliderX, y: sliderY, width: sliderWidth, height: sliderHeight},
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/slider.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1,
visible: false
});
// The slider is handled in the mouse event callbacks.
var isMovingSlider = false;
var thumbClickOffsetX = 0;
// This is the thumb of our slider
var minThumbX = 30; // relative to the x of the slider
var maxThumbX = minThumbX + 65;
var thumbExtents = maxThumbX - minThumbX;
var thumbX = (minThumbX + maxThumbX) / 2;
var thumbY = sliderY + 9;
var thumb = Overlays.addOverlay("image", {
x: sliderX + thumbX,
y: thumbY,
width: 18,
height: 17,
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/thumb.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1,
visible: false
});
var pointerVoxelScale = 0; // 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
var pointerVoxelScaleSteps = 8; // the number of slider position steps
var pointerVoxelScaleOriginStep = 3; // 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);
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;
}
thumbX = (thumbDeltaPerStep * (thumbStep - 1)) + minThumbX;
Overlays.editOverlay(thumb, { x: thumbX + sliderX } );
}
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));
// 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 camera = Camera.getPosition();
var forwardVector = Quat.getFront(MyAvatar.orientation);
@ -144,85 +325,225 @@ var trackLastMouseX = 0;
var trackLastMouseY = 0;
var trackAsDelete = false;
var trackAsRecolor = false;
var trackAsEyedropper = false;
var trackAsOrbit = false;
function showPreviewVoxel() {
if (editToolsOn) {
var voxelColor;
function calculateVoxelFromIntersection(intersection, operation) {
//print("calculateVoxelFromIntersection() operation="+operation);
var resultVoxel;
var voxelSize;
if (pointerVoxelScaleSet) {
voxelSize = pointerVoxelScale;
} else {
voxelSize = intersection.voxel.s;
}
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
var intersection = Voxels.findRayIntersection(pickRay);
// first, calculate the enclosed voxel of size voxelSize that the intersection point falls inside of.
// if you have a voxelSize that's smaller than the voxel you're intersecting, this calculation will result
// in the subvoxel that the intersection point falls in
var x = Math.floor(intersection.intersection.x / voxelSize) * voxelSize;
var y = Math.floor(intersection.intersection.y / voxelSize) * voxelSize;
var z = Math.floor(intersection.intersection.z / voxelSize) * voxelSize;
resultVoxel = { x: x, y: y, z: z, s: voxelSize };
highlightAt = { x: x, y: y, z: z, s: voxelSize };
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 };
// now we also want to calculate the "edge square" for the face for this voxel
if (intersection.face == "MIN_X_FACE") {
highlightAt.x = intersection.voxel.x;
resultVoxel.x = intersection.voxel.x;
if (operation == "add") {
resultVoxel.x -= voxelSize;
}
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize };
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z + voxelSize };
} else if (intersection.face == "MAX_X_FACE") {
highlightAt.x = intersection.voxel.x + intersection.voxel.s;
resultVoxel.x = intersection.voxel.x + intersection.voxel.s;
if (operation != "add") {
resultVoxel.x -= voxelSize;
}
var guidePosition;
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize };
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z + voxelSize };
} else if (intersection.face == "MIN_Y_FACE") {
highlightAt.y = intersection.voxel.y;
resultVoxel.y = intersection.voxel.y;
if (trackAsDelete) {
guidePosition = { x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z };
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: intersection.voxel.s,
visible: true,
color: { red: 255, green: 0, blue: 0 },
solid: false,
alpha: 1
});
} else if (trackAsRecolor) {
guidePosition = { x: intersection.voxel.x - 0.001,
y: intersection.voxel.y - 0.001,
z: intersection.voxel.z - 0.001 };
if (operation == "add") {
resultVoxel.y -= voxelSize;
}
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z};
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize };
resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z + voxelSize};
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: intersection.voxel.s + 0.002,
visible: true,
color: voxelColor,
solid: true,
alpha: 0.8
});
} else if (intersection.face == "MAX_Y_FACE") {
} else if (!isExtruding) {
guidePosition = { x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z };
if (intersection.face == "MIN_X_FACE") {
guidePosition.x -= intersection.voxel.s;
} else if (intersection.face == "MAX_X_FACE") {
guidePosition.x += intersection.voxel.s;
} else if (intersection.face == "MIN_Y_FACE") {
guidePosition.y -= intersection.voxel.s;
} else if (intersection.face == "MAX_Y_FACE") {
guidePosition.y += intersection.voxel.s;
} else if (intersection.face == "MIN_Z_FACE") {
guidePosition.z -= intersection.voxel.s;
} else if (intersection.face == "MAX_Z_FACE") {
guidePosition.z += intersection.voxel.s;
}
highlightAt.y = intersection.voxel.y + intersection.voxel.s;
resultVoxel.y = intersection.voxel.y + intersection.voxel.s;
if (operation != "add") {
resultVoxel.y -= voxelSize;
}
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z};
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize };
resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z + voxelSize};
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: intersection.voxel.s,
visible: true,
color: voxelColor,
solid: true,
alpha: 0.7
});
} else if (isExtruding) {
} else if (intersection.face == "MIN_Z_FACE") {
highlightAt.z = intersection.voxel.z;
resultVoxel.z = intersection.voxel.z;
if (operation == "add") {
resultVoxel.z -= voxelSize;
}
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z};
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y + voxelSize, z: highlightAt.z};
} else if (intersection.face == "MAX_Z_FACE") {
highlightAt.z = intersection.voxel.z + intersection.voxel.s;
resultVoxel.z = intersection.voxel.z + intersection.voxel.s;
if (operation != "add") {
resultVoxel.z -= voxelSize;
}
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z};
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
resultVoxel.topRight = {x: highlightAt.x + voxelSize , y: highlightAt.y + voxelSize, z: highlightAt.z};
}
return resultVoxel;
}
function showPreviewVoxel() {
var voxelColor;
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 (trackAsDelete) {
guidePosition = calculateVoxelFromIntersection(intersection,"delete");
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: guidePosition.s,
visible: true,
color: { red: 255, green: 0, blue: 0 },
solid: false,
alpha: 1
});
} else if (trackAsRecolor || trackAsEyedropper) {
guidePosition = calculateVoxelFromIntersection(intersection,"recolor");
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: guidePosition.s + 0.002,
visible: true,
color: voxelColor,
solid: true,
alpha: 0.8
});
} else if (trackAsOrbit) {
Overlays.editOverlay(voxelPreview, { visible: false });
} else if (!isExtruding) {
guidePosition = calculateVoxelFromIntersection(intersection,"add");
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: guidePosition.s,
visible: true,
color: voxelColor,
solid: true,
alpha: 0.7
});
} else if (isExtruding) {
Overlays.editOverlay(voxelPreview, { visible: false });
}
}
function showPreviewLines() {
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
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(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 });
} else {
Overlays.editOverlay(linePreviewTop, { visible: false });
Overlays.editOverlay(linePreviewBottom, { visible: false });
Overlays.editOverlay(linePreviewLeft, { visible: false });
Overlays.editOverlay(linePreviewRight, { visible: false });
}
}
function showPreviewGuides() {
if (editToolsOn) {
if (previewAsVoxel) {
showPreviewVoxel();
// make sure alternative is hidden
Overlays.editOverlay(linePreviewTop, { visible: false });
Overlays.editOverlay(linePreviewBottom, { visible: false });
Overlays.editOverlay(linePreviewLeft, { visible: false });
Overlays.editOverlay(linePreviewRight, { visible: false });
} else {
showPreviewLines();
// make sure alternative is hidden
Overlays.editOverlay(voxelPreview, { visible: false });
}
} else {
// make sure all previews are off
Overlays.editOverlay(voxelPreview, { visible: false });
Overlays.editOverlay(linePreviewTop, { visible: false });
Overlays.editOverlay(linePreviewBottom, { visible: false });
Overlays.editOverlay(linePreviewLeft, { visible: false });
Overlays.editOverlay(linePreviewRight, { visible: false });
}
}
@ -231,27 +552,58 @@ function trackMouseEvent(event) {
trackLastMouseY = event.y;
trackAsDelete = event.isControl;
trackAsRecolor = event.isShifted;
showPreviewVoxel();
trackAsEyedropper = event.isMeta;
trackAsOrbit = event.isAlt;
showPreviewGuides();
}
function trackKeyPressEvent(event) {
if (event.text == "CONTROL") {
trackAsDelete = true;
showPreviewVoxel();
moveTools();
}
if (event.text == "SHIFT") {
trackAsRecolor = true;
moveTools();
}
showPreviewVoxel();
if (event.text == "META") {
trackAsEyedropper = true;
moveTools();
}
if (event.text == "ALT") {
trackAsOrbit = true;
moveTools();
}
showPreviewGuides();
}
function trackKeyReleaseEvent(event) {
if (event.text == "ESC") {
pointerVoxelScaleSet = false;
}
if (event.text == "-") {
thumbX -= thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
}
if (event.text == "+") {
thumbX += thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
}
if (event.text == "CONTROL") {
trackAsDelete = false;
showPreviewVoxel();
moveTools();
}
if (event.text == "SHIFT") {
trackAsRecolor = false;
moveTools();
}
if (event.text == "META") {
trackAsEyedropper = false;
moveTools();
}
if (event.text == "ALT") {
trackAsOrbit = false;
moveTools();
}
// on TAB release, toggle our tool state
@ -260,7 +612,13 @@ function trackKeyReleaseEvent(event) {
moveTools();
Audio.playSound(clickSound, audioOptions);
}
showPreviewVoxel();
// on F1 toggle the preview mode between cubes and lines
if (event.text == "F1") {
previewAsVoxel = !previewAsVoxel;
}
showPreviewGuides();
}
function mousePressEvent(event) {
@ -269,11 +627,30 @@ function mousePressEvent(event) {
if (!editToolsOn) {
return;
}
var clickedOnSwatch = false;
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
if (event.isRightButton) {
// debugging of right button click on mac...
print(">>>> RIGHT BUTTON <<<<<");
// 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
return; // no further processing
} else {
// if the user clicked on one of the color swatches, update the selectedSwatch
for (s = 0; s < numColors; s++) {
if (clickedOverlay == swatches[s]) {
whichColor = s;
moveTools();
clickedOnSwatch = true;
}
}
if (clickedOnSwatch) {
return; // no further processing
}
}
trackMouseEvent(event); // used by preview support
mouseX = event.x;
mouseY = event.y;
@ -281,6 +658,11 @@ function mousePressEvent(event) {
var intersection = Voxels.findRayIntersection(pickRay);
audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction);
if (intersection.intersects) {
// if the user hasn't updated the
if (!pointerVoxelScaleSet) {
calcThumbFromScale(intersection.voxel.s);
}
if (event.isAlt) {
// start orbit camera!
var cameraPosition = Camera.getPosition();
@ -295,18 +677,27 @@ function mousePressEvent(event) {
orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x);
orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector));
} else if (trackAsDelete || event.isRightButton) {
} else if (trackAsDelete || (event.isRightButton && !trackAsEyedropper)) {
// Delete voxel
Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
voxelDetails = calculateVoxelFromIntersection(intersection,"delete");
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
Audio.playSound(deleteSound, audioOptions);
Overlays.editOverlay(voxelPreview, { visible: false });
} else if (trackAsEyedropper) {
if (whichColor != -1) {
colors[whichColor].red = intersection.voxel.red;
colors[whichColor].green = intersection.voxel.green;
colors[whichColor].blue = intersection.voxel.blue;
moveTools();
}
} else if (trackAsRecolor) {
// Recolor Voxel
Voxels.setVoxel(intersection.voxel.x,
intersection.voxel.y,
intersection.voxel.z,
intersection.voxel.s,
voxelDetails = calculateVoxelFromIntersection(intersection,"recolor");
// doing this erase then set will make sure we only recolor just the target voxel
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s,
colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue);
Audio.playSound(changeColorSound, audioOptions);
Overlays.editOverlay(voxelPreview, { visible: false });
@ -314,43 +705,23 @@ function mousePressEvent(event) {
// Add voxel on face
if (whichColor == -1) {
// Copy mode - use clicked voxel color
var newVoxel = {
x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z,
s: intersection.voxel.s,
newColor = {
red: intersection.voxel.red,
green: intersection.voxel.green,
blue: intersection.voxel.blue };
} else {
var newVoxel = {
x: intersection.voxel.x,
y: intersection.voxel.y,
z: intersection.voxel.z,
s: intersection.voxel.s,
newColor = {
red: colors[whichColor].red,
green: colors[whichColor].green,
blue: colors[whichColor].blue };
}
if (intersection.face == "MIN_X_FACE") {
newVoxel.x -= newVoxel.s;
} else if (intersection.face == "MAX_X_FACE") {
newVoxel.x += newVoxel.s;
} else if (intersection.face == "MIN_Y_FACE") {
newVoxel.y -= newVoxel.s;
} else if (intersection.face == "MAX_Y_FACE") {
newVoxel.y += newVoxel.s;
} else if (intersection.face == "MIN_Z_FACE") {
newVoxel.z -= newVoxel.s;
} else if (intersection.face == "MAX_Z_FACE") {
newVoxel.z += newVoxel.s;
}
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
lastVoxelPosition = { x: newVoxel.x, y: newVoxel.y, z: newVoxel.z };
lastVoxelColor = { red: newVoxel.red, green: newVoxel.green, blue: newVoxel.blue };
lastVoxelScale = newVoxel.s;
voxelDetails = calculateVoxelFromIntersection(intersection,"add");
Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s,
newColor.red, newColor.green, newColor.blue);
lastVoxelPosition = { x: voxelDetails.x, y: voxelDetails.y, z: voxelDetails.z };
lastVoxelColor = { red: newColor.red, green: newColor.green, blue: newColor.blue };
lastVoxelScale = voxelDetails.s;
Audio.playSound(addSound, audioOptions);
Overlays.editOverlay(voxelPreview, { visible: false });
@ -410,8 +781,20 @@ function keyReleaseEvent(event) {
key_alt = false;
key_shift = false;
}
function mouseMoveEvent(event) {
if (isOrbiting) {
if (isMovingSlider) {
thumbX = (event.x - thumbClickOffsetX) - sliderX;
if (thumbX < minThumbX) {
thumbX = minThumbX;
}
if (thumbX > maxThumbX) {
thumbX = maxThumbX;
}
calcScaleFromThumb(thumbX);
} else if (isOrbiting) {
var cameraOrientation = Camera.getOrientation();
var origEulers = Quat.safeEulerAngles(cameraOrientation);
var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation));
@ -426,8 +809,7 @@ function mouseMoveEvent(event) {
Camera.setPosition(orbitPosition);
mouseX = event.x;
mouseY = event.y;
}
if (isAdding) {
} else if (isAdding) {
// Watch the drag direction to tell which way to 'extrude' this voxel
if (!isExtruding) {
var pickRay = Camera.computePickRay(event.x, event.y);
@ -476,6 +858,10 @@ function mouseReleaseEvent(event) {
return;
}
if (isMovingSlider) {
isMovingSlider = false;
}
if (isOrbiting) {
var cameraOrientation = Camera.getOrientation();
var eulers = Quat.safeEulerAngles(cameraOrientation);
@ -491,7 +877,8 @@ function mouseReleaseEvent(event) {
}
function moveTools() {
swatchesX = (windowDimensions.x - swatchesWidth) / 2;
// move the swatches
swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
swatchesY = windowDimensions.y - swatchHeight;
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
@ -513,6 +900,66 @@ function moveTools() {
visible: editToolsOn
});
}
// move the tools
toolsY = (windowDimensions.y - toolsHeight) / 2;
addToolColor = notSelectedColor;
deleteToolColor = notSelectedColor;
recolorToolColor = notSelectedColor;
eyedropperToolColor = notSelectedColor;
selectToolColor = notSelectedColor;
if (trackAsDelete) {
deleteToolColor = toolSelectedColor;
} else if (trackAsRecolor) {
recolorToolColor = toolSelectedColor;
} else if (trackAsEyedropper) {
eyedropperToolColor = toolSelectedColor;
} else if (trackAsOrbit) {
// nothing gets selected in this case...
} else {
addToolColor = toolSelectedColor;
}
Overlays.editOverlay(addTool, {
x: 0, y: toolsY + (toolHeight * addToolAt), width: toolWidth, height: toolHeight,
color: addToolColor,
visible: editToolsOn
});
Overlays.editOverlay(deleteTool, {
x: 0, y: toolsY + (toolHeight * deleteToolAt), width: toolWidth, height: toolHeight,
color: deleteToolColor,
visible: editToolsOn
});
Overlays.editOverlay(recolorTool, {
x: 0, y: toolsY + (toolHeight * recolorToolAt), width: toolWidth, height: toolHeight,
color: recolorToolColor,
visible: editToolsOn
});
Overlays.editOverlay(eyedropperTool, {
x: 0, y: toolsY + (toolHeight * eyedropperToolAt), width: toolWidth, height: toolHeight,
color: eyedropperToolColor,
visible: editToolsOn
});
Overlays.editOverlay(selectTool, {
x: 0, y: toolsY + (toolHeight * selectToolAt), width: toolWidth, height: toolHeight,
color: selectToolColor,
visible: editToolsOn
});
sliderX = swatchesX + swatchesWidth;
sliderY = windowDimensions.y - sliderHeight;
Overlays.editOverlay(slider, { x: sliderX, y: sliderY, visible: editToolsOn });
// This is the thumb of our slider
thumbY = sliderY + 9;
Overlays.editOverlay(thumb, { x: sliderX + thumbX, y: thumbY, visible: editToolsOn });
}
@ -520,7 +967,6 @@ function update() {
var newWindowDimensions = Controller.getViewportDimensions();
if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) {
windowDimensions = newWindowDimensions;
print("window resized...");
moveTools();
}
}
@ -533,9 +979,18 @@ Controller.keyReleaseEvent.connect(keyReleaseEvent);
function scriptEnding() {
Overlays.deleteOverlay(voxelPreview);
Overlays.deleteOverlay(linePreviewTop);
Overlays.deleteOverlay(linePreviewBottom);
Overlays.deleteOverlay(linePreviewLeft);
Overlays.deleteOverlay(linePreviewRight);
for (s = 0; s < numColors; s++) {
Overlays.deleteOverlay(swatches[s]);
}
Overlays.deleteOverlay(addTool);
Overlays.deleteOverlay(deleteTool);
Overlays.deleteOverlay(recolorTool);
Overlays.deleteOverlay(eyedropperTool);
Overlays.deleteOverlay(selectTool);
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -62,6 +62,7 @@
#include <VoxelSceneStats.h>
#include "Application.h"
#include "ClipboardScriptingInterface.h"
#include "DataServerClient.h"
#include "InterfaceVersion.h"
#include "Menu.h"
@ -1699,6 +1700,10 @@ bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) {
}
void Application::exportVoxels() {
exportVoxels(_mouseVoxel);
}
void Application::exportVoxels(const VoxelDetail& sourceVoxel) {
QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
QString suggestedName = desktopLocation.append("/voxels.svo");
@ -1706,7 +1711,7 @@ void Application::exportVoxels() {
tr("Sparse Voxel Octree Files (*.svo)"));
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
const char* fileName = fileNameAscii.data();
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
if (selectedNode) {
VoxelTree exportTree;
_voxels.copySubTreeIntoNewTree(selectedNode, &exportTree, true);
@ -1740,11 +1745,19 @@ void Application::importVoxels() {
}
void Application::cutVoxels() {
copyVoxels();
deleteVoxelUnderCursor();
cutVoxels(_mouseVoxel);
}
void Application::cutVoxels(const VoxelDetail& sourceVoxel) {
copyVoxels(sourceVoxel);
deleteVoxelAt(sourceVoxel);
}
void Application::copyVoxels() {
copyVoxels(_mouseVoxel);
}
void Application::copyVoxels(const VoxelDetail& sourceVoxel) {
// switch to and clear the clipboard first...
_sharedVoxelSystem.killLocalVoxels();
if (_sharedVoxelSystem.getTree() != &_clipboard) {
@ -1753,7 +1766,7 @@ void Application::copyVoxels() {
}
// then copy onto it if there is something to copy
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
if (selectedNode) {
_voxels.copySubTreeIntoNewTree(selectedNode, &_sharedVoxelSystem, true);
}
@ -1775,8 +1788,12 @@ void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestinati
}
void Application::pasteVoxels() {
pasteVoxels(_mouseVoxel);
}
void Application::pasteVoxels(const VoxelDetail& sourceVoxel) {
unsigned char* calculatedOctCode = NULL;
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
// we only need the selected voxel to get the newBaseOctCode, which we can actually calculate from the
// voxel size/position details. If we don't have an actual selectedNode then use the mouseVoxel to create a
@ -1785,7 +1802,7 @@ void Application::pasteVoxels() {
if (selectedNode) {
octalCodeDestination = selectedNode->getOctalCode();
} else {
octalCodeDestination = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
octalCodeDestination = calculatedOctCode = pointToVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
}
pasteVoxelsToOctalCode(octalCodeDestination);
@ -1830,12 +1847,14 @@ void Application::nudgeVoxels() {
// calculate nudgeVec
glm::vec3 nudgeVec(_nudgeGuidePosition.x - _nudgeVoxel.x, _nudgeGuidePosition.y - _nudgeVoxel.y, _nudgeGuidePosition.z - _nudgeVoxel.z);
VoxelTreeElement* nodeToNudge = _voxels.getVoxelAt(_nudgeVoxel.x, _nudgeVoxel.y, _nudgeVoxel.z, _nudgeVoxel.s);
nudgeVoxelsByVector(_nudgeVoxel, nudgeVec);
}
}
if (nodeToNudge) {
_voxels.getTree()->nudgeSubTree(nodeToNudge, nudgeVec, _voxelEditSender);
_nudgeStarted = false;
}
void Application::nudgeVoxelsByVector(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) {
VoxelTreeElement* nodeToNudge = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
if (nodeToNudge) {
_voxels.getTree()->nudgeSubTree(nodeToNudge, nudgeVec, _voxelEditSender);
}
}
@ -3840,18 +3859,27 @@ bool Application::maybeEditVoxelUnderCursor() {
}
void Application::deleteVoxelUnderCursor() {
if (_mouseVoxel.s != 0) {
deleteVoxelAt(_mouseVoxel);
}
void Application::deleteVoxels(const VoxelDetail& voxel) {
deleteVoxelAt(voxel);
}
void Application::deleteVoxelAt(const VoxelDetail& voxel) {
if (voxel.s != 0) {
// sending delete to the server is sufficient, server will send new version so we see updates soon enough
_voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, _mouseVoxel);
_voxelEditSender.sendVoxelEditMessage(PacketTypeVoxelErase, voxel);
// delete it locally to see the effect immediately (and in case no voxel server is present)
_voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
_voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
}
// remember the position for drag detection
_justEditedVoxel = true;
}
void Application::eyedropperVoxelUnderCursor() {
VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
if (selectedNode && selectedNode->isColored()) {
@ -4190,6 +4218,10 @@ void Application::loadScript(const QString& fileNameString) {
scriptEngine->registerGlobalObject("Camera", cameraScriptable);
connect(scriptEngine, SIGNAL(finished(const QString&)), cameraScriptable, SLOT(deleteLater()));
ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface();
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
scriptEngine->registerGlobalObject("Overlays", &_overlays);
QThread* workerThread = new QThread(this);

View file

@ -227,13 +227,20 @@ public slots:
void nodeKilled(SharedNodePointer node);
void packetSent(quint64 length);
void exportVoxels();
void importVoxels();
void cutVoxels();
void copyVoxels();
void pasteVoxels();
void nudgeVoxels();
void deleteVoxels();
void exportVoxels();
void importVoxels();
void nudgeVoxels();
void cutVoxels(const VoxelDetail& sourceVoxel);
void copyVoxels(const VoxelDetail& sourceVoxel);
void pasteVoxels(const VoxelDetail& sourceVoxel);
void deleteVoxels(const VoxelDetail& sourceVoxel);
void exportVoxels(const VoxelDetail& sourceVoxel);
void nudgeVoxelsByVector(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec);
void setRenderVoxels(bool renderVoxels);
void doKillLocalVoxels();
@ -328,6 +335,7 @@ private:
bool maybeEditVoxelUnderCursor();
void deleteVoxelUnderCursor();
void deleteVoxelAt(const VoxelDetail& voxel);
void eyedropperVoxelUnderCursor();
void setMenuShortcutsEnabled(bool enabled);

View file

@ -0,0 +1,94 @@
//
// ClipboardScriptingInterface.cpp
// interface
//
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
#include "Application.h"
#include "ClipboardScriptingInterface.h"
ClipboardScriptingInterface::ClipboardScriptingInterface() {
}
void ClipboardScriptingInterface::cutVoxel(const VoxelDetail& sourceVoxel) {
cutVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
}
void ClipboardScriptingInterface::cutVoxel(float x, float y, float z, float s) {
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
y / (float)TREE_SCALE,
z / (float)TREE_SCALE,
s / (float)TREE_SCALE };
Application::getInstance()->cutVoxels(sourceVoxel);
}
void ClipboardScriptingInterface::copyVoxel(const VoxelDetail& sourceVoxel) {
copyVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
}
void ClipboardScriptingInterface::copyVoxel(float x, float y, float z, float s) {
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
y / (float)TREE_SCALE,
z / (float)TREE_SCALE,
s / (float)TREE_SCALE };
Application::getInstance()->copyVoxels(sourceVoxel);
}
void ClipboardScriptingInterface::pasteVoxel(const VoxelDetail& destinationVoxel) {
pasteVoxel(destinationVoxel.x, destinationVoxel.y, destinationVoxel.z, destinationVoxel.s);
}
void ClipboardScriptingInterface::pasteVoxel(float x, float y, float z, float s) {
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
y / (float)TREE_SCALE,
z / (float)TREE_SCALE,
s / (float)TREE_SCALE };
Application::getInstance()->pasteVoxels(sourceVoxel);
}
void ClipboardScriptingInterface::deleteVoxel(const VoxelDetail& sourceVoxel) {
deleteVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
}
void ClipboardScriptingInterface::deleteVoxel(float x, float y, float z, float s) {
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
y / (float)TREE_SCALE,
z / (float)TREE_SCALE,
s / (float)TREE_SCALE };
Application::getInstance()->deleteVoxels(sourceVoxel);
}
void ClipboardScriptingInterface::exportVoxel(const VoxelDetail& sourceVoxel) {
exportVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s);
}
void ClipboardScriptingInterface::exportVoxel(float x, float y, float z, float s) {
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
y / (float)TREE_SCALE,
z / (float)TREE_SCALE,
s / (float)TREE_SCALE };
// TODO: should we be calling invokeMethod() in all these cases?
Application::getInstance()->exportVoxels(sourceVoxel);
}
void ClipboardScriptingInterface::importVoxels() {
QMetaObject::invokeMethod(Application::getInstance(), "importVoxels");
}
void ClipboardScriptingInterface::nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) {
nudgeVoxel(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s, nudgeVec);
}
void ClipboardScriptingInterface::nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec) {
glm::vec3 nudgeVecInTreeSpace = nudgeVec / (float)TREE_SCALE;
VoxelDetail sourceVoxel = { x / (float)TREE_SCALE,
y / (float)TREE_SCALE,
z / (float)TREE_SCALE,
s / (float)TREE_SCALE };
Application::getInstance()->nudgeVoxelsByVector(sourceVoxel, nudgeVecInTreeSpace);
}

View file

@ -0,0 +1,43 @@
//
// ClipboardScriptingInterface.h
// interface
//
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
// Scriptable interface for the Application clipboard
//
#ifndef __interface__Clipboard__
#define __interface__Clipboard__
#include <QObject>
#include <VoxelDetail.h>
class ClipboardScriptingInterface : public QObject {
Q_OBJECT
public:
ClipboardScriptingInterface();
public slots:
void cutVoxel(const VoxelDetail& sourceVoxel);
void cutVoxel(float x, float y, float z, float s);
void copyVoxel(const VoxelDetail& sourceVoxel);
void copyVoxel(float x, float y, float z, float s);
void pasteVoxel(const VoxelDetail& destinationVoxel);
void pasteVoxel(float x, float y, float z, float s);
void deleteVoxel(const VoxelDetail& sourceVoxel);
void deleteVoxel(float x, float y, float z, float s);
void exportVoxel(const VoxelDetail& sourceVoxel);
void exportVoxel(float x, float y, float z, float s);
void importVoxels();
void nudgeVoxel(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec);
void nudgeVoxel(float x, float y, float z, float s, const glm::vec3& nudgeVec);
};
#endif // __interface__Clipboard__

View file

@ -104,6 +104,16 @@ KeyEvent::KeyEvent(const QKeyEvent& event) {
text = "HELP";
} else if (key == Qt::Key_CapsLock) {
text = "CAPS LOCK";
} else if (key >= Qt::Key_A && key <= Qt::Key_Z && (isMeta || isControl || isAlt)) {
// this little bit of hackery will fix the text character keys like a-z in cases of control/alt/meta where
// qt doesn't always give you the key characters and will sometimes give you crazy non-printable characters
const int lowerCaseAdjust = 0x20;
QString unicode;
if (isShifted) {
text = QString(QChar(key));
} else {
text = QString(QChar(key + lowerCaseAdjust));
}
}
}

View file

@ -229,6 +229,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
// send back a reply
SharedNodePointer matchingNode = sendingNodeForPacket(packet);
if (matchingNode) {
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
QByteArray replyPacket = constructPingReplyPacket(packet);
writeDatagram(replyPacket, matchingNode, senderSockAddr);
}
@ -239,6 +240,8 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
if (sendingNode) {
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
// activate the appropriate socket for this node, if not yet updated
activateSocketFromNodeCommunication(packet, sendingNode);

View file

@ -22,7 +22,7 @@
/// PacketTypeVoxelSet, PacketTypeVoxelSetDestructive, or PacketTypeVoxelErase. The buffer is returned to caller becomes
/// responsibility of caller and MUST be deleted by caller.
bool createVoxelEditMessage(PacketType command, short int sequence,
int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) {
int voxelCount, const VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) {
bool success = true; // assume the best
int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now
@ -102,7 +102,7 @@ bool encodeVoxelEditMessageDetails(PacketType, int voxelCount, VoxelDetail* voxe
return success;
}
void VoxelEditPacketSender::sendVoxelEditMessage(PacketType type, VoxelDetail& detail) {
void VoxelEditPacketSender::sendVoxelEditMessage(PacketType type, const VoxelDetail& detail) {
// allows app to disable sending if for example voxels have been disabled
if (!_shouldSend) {
return; // bail early

View file

@ -19,7 +19,7 @@ class VoxelEditPacketSender : public OctreeEditPacketSender {
Q_OBJECT
public:
/// Send voxel edit message immediately
void sendVoxelEditMessage(PacketType type, VoxelDetail& detail);
void sendVoxelEditMessage(PacketType type, const VoxelDetail& detail);
/// Queues a single voxel edit message. Will potentially send a pending multi-command packet. Determines which voxel-server
/// node or nodes the packet should be sent to. Can be called even before voxel servers are known, in which case up to