mirror of
https://github.com/overte-org/overte.git
synced 2025-04-24 03:33:35 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into clipboard_overlay
This commit is contained in:
commit
d06f526bf4
11 changed files with 934 additions and 135 deletions
|
@ -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);
|
||||
}
|
||||
|
|
151
examples/clipboardExample.js
Normal file
151
examples/clipboardExample.js
Normal 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);
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
94
interface/src/ClipboardScriptingInterface.cpp
Normal file
94
interface/src/ClipboardScriptingInterface.cpp
Normal 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);
|
||||
}
|
||||
|
43
interface/src/ClipboardScriptingInterface.h
Normal file
43
interface/src/ClipboardScriptingInterface.h
Normal 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__
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue