overte/examples/editVoxels.js

1397 lines
55 KiB
JavaScript

//
// editVoxels.js
// hifi
//
// Created by Philip Rosedale on February 8, 2014
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// Captures mouse clicks and edits voxels accordingly.
//
// click = create a new voxel on this face, same color as old (default color picker state)
// right click or control + click = delete this voxel
// shift + click = recolor this voxel
// 1 - 8 = pick new color from palette
// 9 = create a new voxel in front of the camera
//
// Click and drag to create more new voxels in the same direction
//
var windowDimensions = Controller.getViewportDimensions();
var NEW_VOXEL_SIZE = 1.0;
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
var ORBIT_RATE_ALTITUDE = 200.0;
var ORBIT_RATE_AZIMUTH = 90.0;
var PIXELS_PER_EXTRUDE_VOXEL = 16;
var WHEEL_PIXELS_PER_SCALE_CHANGE = 100;
var MAX_VOXEL_SCALE = 1.0;
var MIN_VOXEL_SCALE = 1.0 / Math.pow(2.0, 8.0);
var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting
var previewLineWidth = 1.5;
var oldMode = Camera.getMode();
var isAdding = false;
var isExtruding = false;
var isOrbiting = false;
var isOrbitingFromTouch = false;
var isPanning = false;
var isPanningFromTouch = false;
var touchPointsToOrbit = 2; // you can change these, but be mindful that on some track pads 2 touch points = right click+drag
var touchPointsToPan = 3;
var orbitAzimuth = 0.0;
var orbitAltitude = 0.0;
var orbitCenter = { x: 0, y: 0, z: 0 };
var orbitPosition = { x: 0, y: 0, z: 0 };
var torsoToEyeVector = { x: 0, y: 0, z: 0 };
var orbitRadius = 0.0;
var extrudeDirection = { x: 0, y: 0, z: 0 };
var extrudeScale = 0.0;
var lastVoxelPosition = { x: 0, y: 0, z: 0 };
var lastVoxelColor = { red: 0, green: 0, blue: 0 };
var lastVoxelScale = 0;
var dragStart = { x: 0, y: 0 };
var wheelPixelsMoved = 0;
var mouseX = 0;
var mouseY = 0;
// Create a table of the different colors you can choose
var colors = new Array();
colors[0] = { red: 120, green: 181, blue: 126 };
colors[1] = { red: 75, green: 155, blue: 103 };
colors[2] = { red: 56, green: 132, blue: 86 };
colors[3] = { red: 83, green: 211, blue: 83 };
colors[4] = { red: 236, green: 174, blue: 0 };
colors[5] = { red: 234, green: 133, blue: 0 };
colors[6] = { red: 211, green: 115, blue: 0 };
colors[7] = { red: 48, green: 116, blue: 119 };
colors[8] = { red: 31, green: 64, blue: 64 };
var numColors = 9;
var whichColor = -1; // Starting color is 'Copy' mode
// Create sounds for adding, deleting, recoloring voxels
var addSound1 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+2.raw");
var addSound2 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+3.raw");
var addSound3 = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create+4.raw");
var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+delete+2.raw");
var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+edit+2.raw");
var clickSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Switches+and+sliders/toggle+switch+-+medium.raw");
var audioOptions = new AudioInjectionOptions();
audioOptions.volume = 0.5;
audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0 } ); // start with audio slightly above the avatar
var editToolsOn = true; // starts out off
// previewAsVoxel - by default, we will preview adds/deletes/recolors as just 4 lines on the intersecting face. But if you
// the preview to show a full voxel then set this to true and the voxel will be displayed for voxel editing
var previewAsVoxel = false;
var voxelPreview = Overlays.addOverlay("cube", {
position: { x: 0, y: 0, z: 0},
size: 1,
color: { red: 255, green: 0, blue: 0},
alpha: 1,
solid: false,
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: previewLineWidth
});
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: previewLineWidth
});
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: previewLineWidth
});
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: previewLineWidth
});
// these will be used below
var sliderWidth = 154;
var sliderHeight = 37;
// These will be our "overlay IDs"
var swatches = new Array();
var swatchExtraPadding = 5;
var swatchHeight = 37;
var swatchWidth = 27;
var swatchesWidth = swatchWidth * numColors + numColors + swatchExtraPadding * 2;
var swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
var swatchesY = windowDimensions.y - swatchHeight + 1;
var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/";
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
// location so that it displays the "selected" marker
for (s = 0; s < numColors; s++) {
var extraWidth = 0;
if (s == 0) {
extraWidth = swatchExtraPadding;
}
var imageFromX = swatchExtraPadding - extraWidth + s * swatchWidth;
var imageFromY = swatchHeight + 1;
var swatchX = swatchExtraPadding - extraWidth + swatchesX + ((swatchWidth - 1) * s);
if (s == (numColors - 1)) {
extraWidth = swatchExtraPadding;
}
swatches[s] = Overlays.addOverlay("image", {
x: swatchX,
y: swatchesY,
width: swatchWidth + extraWidth,
height: swatchHeight,
subImage: { x: imageFromX, y: imageFromY, width: swatchWidth + extraWidth, height: swatchHeight },
imageURL: toolIconUrl + "swatches.svg",
color: colors[s],
alpha: 1,
visible: editToolsOn
});
}
// These will be our tool palette overlays
var numberOfTools = 3;
var toolHeight = 50;
var toolWidth = 50;
var toolVerticalSpacing = 4;
var toolsHeight = toolHeight * numberOfTools + toolVerticalSpacing * (numberOfTools - 1);
var toolsX = 8;
var toolsY = (windowDimensions.y - toolsHeight) / 2;
var voxelToolAt = 0;
var recolorToolAt = 1;
var eyedropperToolAt = 2;
var voxelTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
imageURL: toolIconUrl + "voxel-tool.svg",
visible: false,
alpha: 0.9
});
var recolorTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
imageURL: toolIconUrl + "paint-tool.svg",
visible: false,
alpha: 0.9
});
var eyedropperTool = Overlays.addOverlay("image", {
x: 0, y: 0, width: toolWidth, height: toolHeight,
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
imageURL: toolIconUrl + "eyedropper-tool.svg",
visible: false,
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 sliderOffsetX = 17;
var sliderX = swatchesX - swatchWidth - sliderOffsetX;
var sliderY = windowDimensions.y - sliderHeight + 1;
var slider = Overlays.addOverlay("image", {
// alternate form of expressing bounds
bounds: { x: sliderX, y: sliderY, width: sliderWidth, height: sliderHeight},
imageURL: toolIconUrl + "voxel-size-slider-bg.svg",
alpha: 1,
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 = 20; // relative to the x of the slider
var maxThumbX = minThumbX + 90;
var thumbExtents = maxThumbX - minThumbX;
var thumbX = (minThumbX + maxThumbX) / 2;
var thumbOffsetY = 11;
var thumbY = sliderY + thumbOffsetY;
var thumb = Overlays.addOverlay("image", {
x: sliderX + thumbX,
y: thumbY,
width: 17,
height: 17,
imageURL: toolIconUrl + "voxel-size-slider-handle.svg",
alpha: 1,
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 = 8; // the position of slider for the 1 meter size voxel
var pointerVoxelScaleMin = Math.pow(2, (1-pointerVoxelScaleOriginStep));
var pointerVoxelScaleMax = Math.pow(2, (pointerVoxelScaleSteps-pointerVoxelScaleOriginStep));
var thumbDeltaPerStep = thumbExtents / (pointerVoxelScaleSteps - 1);
if (editToolsOn) {
moveTools();
}
function calcThumbFromScale(scale) {
var scaleLog = Math.log(scale)/Math.log(2);
var thumbStep = scaleLog + pointerVoxelScaleOriginStep;
if (thumbStep < 1) {
thumbStep = 1;
}
if (thumbStep > pointerVoxelScaleSteps) {
thumbStep = pointerVoxelScaleSteps;
}
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);
audioOptions.position = Vec3.sum(camera, forwardVector);
}
function getNewVoxelPosition() {
var camera = Camera.getPosition();
var forwardVector = Quat.getFront(MyAvatar.orientation);
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, NEW_VOXEL_DISTANCE_FROM_CAMERA));
return newPosition;
}
function fixEulerAngles(eulers) {
var rVal = { x: 0, y: 0, z: eulers.z };
if (eulers.x >= 90.0) {
rVal.x = 180.0 - eulers.x;
rVal.y = eulers.y - 180.0;
} else if (eulers.x <= -90.0) {
rVal.x = 180.0 - eulers.x;
rVal.y = eulers.y - 180.0;
}
return rVal;
}
var trackLastMouseX = 0;
var trackLastMouseY = 0;
var trackAsDelete = false;
var trackAsRecolor = false;
var trackAsEyedropper = false;
var trackAsOrbitOrPan = false;
var voxelToolSelected = true;
var recolorToolSelected = false;
var eyedropperToolSelected = false;
function playRandomAddSound(audioOptions) {
if (Math.random() < 0.33) {
Audio.playSound(addSound1, audioOptions);
} else if (Math.random() < 0.5) {
Audio.playSound(addSound2, audioOptions);
} else {
Audio.playSound(addSound3, audioOptions);
}
}
function calculateVoxelFromIntersection(intersection, operation) {
//print("calculateVoxelFromIntersection() operation="+operation);
var resultVoxel;
var wantDebug = false;
if (wantDebug) {
print(">>>>> calculateVoxelFromIntersection().... 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 voxelSize;
if (pointerVoxelScaleSet) {
voxelSize = pointerVoxelScale;
} else {
voxelSize = intersection.voxel.s;
}
var x;
var y;
var z;
// if our "target voxel size" is larger than the voxel we intersected with, then we need to find the closest
// ancestor voxel of our target size that contains our intersected voxel.
if (voxelSize > intersection.voxel.s) {
if (wantDebug) {
print("voxelSize > intersection.voxel.s.... choose the larger voxel that encompasses the one selected");
}
x = Math.floor(intersection.voxel.x / voxelSize) * voxelSize;
y = Math.floor(intersection.voxel.y / voxelSize) * voxelSize;
z = Math.floor(intersection.voxel.z / voxelSize) * voxelSize;
} else {
// otherwise, 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, if the target voxelSize matches the intersecting
// voxel this still works and results in returning the intersecting voxel which is what we want
var adjustToCenter = Vec3.multiply(Voxels.getFaceVector(intersection.face), (voxelSize * -0.5));
if (wantDebug) {
print("adjustToCenter=" + adjustToCenter.x + "," + adjustToCenter.y + "," + adjustToCenter.z);
}
var centerOfIntersectingVoxel = Vec3.sum(intersection.intersection, adjustToCenter);
x = Math.floor(centerOfIntersectingVoxel.x / voxelSize) * voxelSize;
y = Math.floor(centerOfIntersectingVoxel.y / voxelSize) * voxelSize;
z = Math.floor(centerOfIntersectingVoxel.z / voxelSize) * voxelSize;
}
resultVoxel = { x: x, y: y, z: z, s: voxelSize };
highlightAt = { x: x, y: y, z: z, s: voxelSize };
// we only do the "add to the face we're pointing at" adjustment, if the operation is an add
// operation, and the target voxel size is equal to or smaller than the intersecting voxel.
var wantAddAdjust = (operation == "add" && (voxelSize <= intersection.voxel.s));
if (wantDebug) {
print("wantAddAdjust="+wantAddAdjust);
}
// now we also want to calculate the "edge square" for the face for this voxel
if (intersection.face == "MIN_X_FACE") {
highlightAt.x = x - zFightingSizeAdjust;
if (wantAddAdjust) {
resultVoxel.x -= voxelSize;
}
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust };
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust };
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
} else if (intersection.face == "MAX_X_FACE") {
highlightAt.x = x + voxelSize + zFightingSizeAdjust;
if (wantAddAdjust) {
resultVoxel.x += resultVoxel.s;
}
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust };
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + zFightingSizeAdjust };
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
} else if (intersection.face == "MIN_Y_FACE") {
highlightAt.y = y - zFightingSizeAdjust;
if (wantAddAdjust) {
resultVoxel.y -= voxelSize;
}
resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust };
resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust };
resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust , y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust };
} else if (intersection.face == "MAX_Y_FACE") {
highlightAt.y = y + voxelSize + zFightingSizeAdjust;
if (wantAddAdjust) {
resultVoxel.y += voxelSize;
}
resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust };
resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + zFightingSizeAdjust};
resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust};
resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y, z: highlightAt.z + voxelSize - zFightingSizeAdjust};
} else if (intersection.face == "MIN_Z_FACE") {
highlightAt.z = z - zFightingSizeAdjust;
if (wantAddAdjust) {
resultVoxel.z -= voxelSize;
}
resultVoxel.bottomRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z };
resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z};
resultVoxel.topRight = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z };
resultVoxel.topLeft = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z};
} else if (intersection.face == "MAX_Z_FACE") {
highlightAt.z = z + voxelSize + zFightingSizeAdjust;
if (wantAddAdjust) {
resultVoxel.z += voxelSize;
}
resultVoxel.bottomLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z };
resultVoxel.bottomRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + zFightingSizeAdjust, z: highlightAt.z};
resultVoxel.topLeft = {x: highlightAt.x + zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, z: highlightAt.z };
resultVoxel.topRight = {x: highlightAt.x + voxelSize - zFightingSizeAdjust, y: highlightAt.y + voxelSize - zFightingSizeAdjust, 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 (trackAsRecolor || recolorToolSelected || trackAsEyedropper || eyedropperToolSelected) {
Overlays.editOverlay(voxelPreview, { visible: true });
} else if (trackAsOrbitOrPan) {
Overlays.editOverlay(voxelPreview, { visible: false });
} else if (voxelToolSelected && !isExtruding) {
Overlays.editOverlay(voxelPreview, { visible: true });
} 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(voxelPreview, { visible: false });
Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true });
Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true });
Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true });
Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true });
} else {
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 });
}
}
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();
}
} 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 });
}
}
function trackMouseEvent(event) {
if (!trackAsOrbitOrPan) {
trackLastMouseX = event.x;
trackLastMouseY = event.y;
trackAsDelete = event.isControl;
trackAsRecolor = event.isShifted;
trackAsEyedropper = event.isMeta;
trackAsOrbitOrPan = event.isAlt; // TODO: double check this...??
showPreviewGuides();
}
}
function trackKeyPressEvent(event) {
if (!editToolsOn) {
return;
}
if (event.text == "CONTROL") {
trackAsDelete = true;
moveTools();
}
if (event.text == "SHIFT") {
trackAsRecolor = true;
moveTools();
}
if (event.text == "META") {
trackAsEyedropper = true;
moveTools();
}
if (event.text == "ALT") {
trackAsOrbitOrPan = true;
moveTools();
}
showPreviewGuides();
}
function trackKeyReleaseEvent(event) {
// on TAB release, toggle our tool state
if (event.text == "TAB") {
editToolsOn = !editToolsOn;
moveTools();
Audio.playSound(clickSound, audioOptions);
}
if (editToolsOn) {
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;
moveTools();
}
if (event.text == "SHIFT") {
trackAsRecolor = false;
moveTools();
}
if (event.text == "META") {
trackAsEyedropper = false;
moveTools();
}
if (event.text == "ALT") {
trackAsOrbitOrPan = false;
moveTools();
}
// on F1 toggle the preview mode between cubes and lines
if (event.text == "F1") {
previewAsVoxel = !previewAsVoxel;
}
showPreviewGuides();
}
}
function startOrbitMode(event) {
mouseX = event.x;
mouseY = event.y;
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Voxels.findRayIntersection(pickRay);
// start orbit camera!
var cameraPosition = Camera.getPosition();
torsoToEyeVector = Vec3.subtract(cameraPosition, MyAvatar.position);
torsoToEyeVector.x = 0.0;
torsoToEyeVector.z = 0.0;
oldMode = Camera.getMode();
Camera.setMode("independent");
Camera.keepLookingAt(intersection.intersection);
// get position for initial azimuth, elevation
orbitCenter = intersection.intersection;
var orbitVector = Vec3.subtract(cameraPosition, orbitCenter);
orbitRadius = Vec3.length(orbitVector);
orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x);
orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector));
//print("startOrbitMode...");
}
function handleOrbitingMove(event) {
var cameraOrientation = Camera.getOrientation();
var origEulers = Quat.safeEulerAngles(cameraOrientation);
var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation));
var dx = event.x - mouseX;
var dy = event.y - mouseY;
orbitAzimuth += dx / ORBIT_RATE_AZIMUTH;
orbitAltitude += dy / ORBIT_RATE_ALTITUDE;
var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius,
y:Math.sin(orbitAltitude) * orbitRadius,
z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius };
orbitPosition = Vec3.sum(orbitCenter, orbitVector);
Camera.setPosition(orbitPosition);
mouseX = event.x;
mouseY = event.y;
//print("handleOrbitingMove...");
}
function endOrbitMode(event) {
var cameraOrientation = Camera.getOrientation();
MyAvatar.position = Vec3.subtract(Camera.getPosition(), torsoToEyeVector);
MyAvatar.headOrientation = cameraOrientation;
Camera.stopLooking();
Camera.setMode(oldMode);
Camera.setOrientation(cameraOrientation);
//print("endOrbitMode...");
}
function startPanMode(event, intersection) {
// start pan camera!
print("handle PAN mode!!!");
}
function handlePanMove(event) {
print("PANNING mode!!! ");
//print("isPanning="+isPanning + " inPanningFromTouch="+isPanningFromTouch + " trackAsOrbitOrPan="+trackAsOrbitOrPan);
}
function endPanMode(event) {
print("ending PAN mode!!!");
}
function mousePressEvent(event) {
// if our tools are off, then don't do anything
if (!editToolsOn) {
return;
}
// Normally, if we're panning or orbiting from touch, ignore these... because our touch takes precedence.
// but In the case of a button="RIGHT" click, we may get some touch messages first, and we actually want to
// cancel any touch mode, and then let the right-click through
if (isOrbitingFromTouch || isPanningFromTouch) {
// if the user is holding the ALT key AND they are clicking the RIGHT button (or on multi-touch doing a two
// finger touch, then we want to let the new panning behavior take over.
// if it's any other case we still want to bail
if (event.button == "RIGHT" && trackAsOrbitOrPan) {
// cancel our current multitouch operation...
if (isOrbitingFromTouch) {
endOrbitMode(event);
isOrbitingFromTouch = false;
}
if (isPanningFromTouch) {
//print("mousePressEvent... calling endPanMode()");
endPanMode(event);
isPanningFromTouch = false;
}
// let things fall through
} else {
return;
}
}
// no clicking on overlays while in panning mode
if (!trackAsOrbitOrPan) {
var clickedOnSomething = false;
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
// If the user clicked on the thumb, handle the slider logic
if (clickedOverlay == thumb) {
isMovingSlider = true;
thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb
clickedOnSomething = true;
Overlays.editOverlay(thumb, { imageURL: toolIconUrl + "voxel-size-slider-handle.svg", });
} else if (clickedOverlay == voxelTool) {
voxelToolSelected = true;
recolorToolSelected = false;
eyedropperToolSelected = false;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == recolorTool) {
voxelToolSelected = false;
recolorToolSelected = true;
eyedropperToolSelected = false;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == eyedropperTool) {
voxelToolSelected = false;
recolorToolSelected = false;
eyedropperToolSelected = true;
moveTools();
clickedOnSomething = true;
} else if (clickedOverlay == slider) {
if (event.x < sliderX + minThumbX) {
thumbX -= thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
}
if (event.x > sliderX + maxThumbX) {
thumbX += thumbDeltaPerStep;
calcScaleFromThumb(thumbX);
}
moveTools();
clickedOnSomething = true;
} else {
// if the user clicked on one of the color swatches, update the selectedSwatch
for (s = 0; s < numColors; s++) {
if (clickedOverlay == swatches[s]) {
whichColor = s;
moveTools();
clickedOnSomething = true;
break;
}
}
}
if (clickedOnSomething) {
return; // no further processing
}
}
// TODO: does any of this stuff need to execute if we're panning or orbiting?
trackMouseEvent(event); // used by preview support
mouseX = event.x;
mouseY = event.y;
var pickRay = Camera.computePickRay(event.x, event.y);
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);
}
// Note: touch and mouse events can cross paths, so we want to ignore any mouse events that would
// start a pan or orbit if we're already doing a pan or orbit via touch...
if ((event.isAlt || trackAsOrbitOrPan) && !(isOrbitingFromTouch || isPanningFromTouch)) {
if (event.isLeftButton && !event.isRightButton) {
startOrbitMode(event);
isOrbiting = true;
} else if (event.isRightButton && !event.isLeftButton) {
startPanMode(event);
isPanning = true;
}
} else if (trackAsDelete || event.isRightButton && !trackAsEyedropper) {
// Delete voxel
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 (eyedropperToolSelected || 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 (recolorToolSelected || trackAsRecolor) {
// Recolor Voxel
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 });
} else if (voxelToolSelected) {
// Add voxel on face
if (whichColor == -1) {
// Copy mode - use clicked voxel color
newColor = {
red: intersection.voxel.red,
green: intersection.voxel.green,
blue: intersection.voxel.blue };
} else {
newColor = {
red: colors[whichColor].red,
green: colors[whichColor].green,
blue: colors[whichColor].blue };
}
voxelDetails = calculateVoxelFromIntersection(intersection,"add");
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
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;
playRandomAddSound(audioOptions);
Overlays.editOverlay(voxelPreview, { visible: false });
dragStart = { x: event.x, y: event.y };
isAdding = true;
}
}
}
function keyPressEvent(event) {
// if our tools are off, then don't do anything
if (editToolsOn) {
var nVal = parseInt(event.text);
if (event.text == "`") {
print("Color = Copy");
whichColor = -1;
Audio.playSound(clickSound, audioOptions);
moveTools();
} else if ((nVal > 0) && (nVal <= numColors)) {
whichColor = nVal - 1;
print("Color = " + (whichColor + 1));
Audio.playSound(clickSound, audioOptions);
moveTools();
} else if (event.text == "0") {
// Create a brand new 1 meter voxel in front of your avatar
var color = whichColor;
if (color == -1) color = 0;
var newPosition = getNewVoxelPosition();
var newVoxel = {
x: newPosition.x,
y: newPosition.y ,
z: newPosition.z,
s: NEW_VOXEL_SIZE,
red: colors[color].red,
green: colors[color].green,
blue: colors[color].blue };
Voxels.eraseVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s);
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
setAudioPosition();
playRandomAddSound(audioOptions);
}
}
trackKeyPressEvent(event); // used by preview support
}
function keyReleaseEvent(event) {
trackKeyReleaseEvent(event); // used by preview support
}
function setupMenus() {
// hook up menus
Menu.menuItemEvent.connect(menuItemEvent);
// add our menuitems
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Voxels", isSeparator: true, beforeItem: "Physics" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Cut", shortcutKey: "CTRL+X", afterItem: "Voxels" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Copy", shortcutKey: "CTRL+C", afterItem: "Cut" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste", shortcutKey: "CTRL+V", afterItem: "Copy" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Nudge", shortcutKey: "CTRL+N", afterItem: "Paste" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Voxels", isSeparator: true, beforeItem: "Settings" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Voxels", shortcutKey: "CTRL+E", afterItem: "Voxels" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Voxels", shortcutKey: "CTRL+I", afterItem: "Export Voxels" });
}
function cleanupMenus() {
// delete our menuitems
Menu.removeSeparator("Edit", "Voxels");
Menu.removeMenuItem("Edit", "Cut");
Menu.removeMenuItem("Edit", "Copy");
Menu.removeMenuItem("Edit", "Paste");
Menu.removeMenuItem("Edit", "Nudge");
Menu.removeMenuItem("Edit", "Delete");
Menu.removeSeparator("File", "Voxels");
Menu.removeMenuItem("File", "Export Voxels");
Menu.removeMenuItem("File", "Import Voxels");
}
function menuItemEvent(menuItem) {
// handle clipboard items
if (selectToolSelected) {
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
var intersection = Voxels.findRayIntersection(pickRay);
selectedVoxel = calculateVoxelFromIntersection(intersection,"select");
if (menuItem == "Copy") {
print("copying...");
Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if (menuItem == "Cut") {
print("cutting...");
Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if (menuItem == "Paste") {
print("pasting...");
Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if (menuItem == "Delete") {
print("deleting...");
Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if (menuItem == "Export Voxels") {
print("export");
Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
}
if (menuItem == "Import Voxels") {
print("import");
Clipboard.importVoxels();
}
if (menuItem == "Nudge") {
print("nudge");
Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 });
}
}
}
function mouseMoveEvent(event) {
if (!editToolsOn) {
return;
}
// if we're panning or orbiting from touch, ignore these... because our touch takes precedence.
if (isOrbitingFromTouch || isPanningFromTouch) {
return;
}
// double check that we didn't accidentally miss a pan or orbit click request
if (trackAsOrbitOrPan && !isPanning && !isOrbiting) {
if (event.isLeftButton && !event.isRightButton) {
startOrbitMode(event);
isOrbiting = true;
}
if (!event.isLeftButton && event.isRightButton) {
startPanMode(event);
isPanning = true;
}
}
if (!trackAsOrbitOrPan && isMovingSlider) {
thumbX = (event.x - thumbClickOffsetX) - sliderX;
if (thumbX < minThumbX) {
thumbX = minThumbX;
}
if (thumbX > maxThumbX) {
thumbX = maxThumbX;
}
calcScaleFromThumb(thumbX);
} else if (isOrbiting) {
handleOrbitingMove(event);
} else if (isPanning) {
handlePanMove(event);
} else if (!trackAsOrbitOrPan && isAdding) {
// Watch the drag direction to tell which way to 'extrude' this voxel
if (!isExtruding) {
var pickRay = Camera.computePickRay(event.x, event.y);
var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x,
y: pickRay.origin.y - lastVoxelPosition.y,
z: pickRay.origin.z - lastVoxelPosition.z };
var distance = Vec3.length(lastVoxelDistance);
var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance };
mouseSpot.x += pickRay.origin.x;
mouseSpot.y += pickRay.origin.y;
mouseSpot.z += pickRay.origin.z;
var dx = mouseSpot.x - lastVoxelPosition.x;
var dy = mouseSpot.y - lastVoxelPosition.y;
var dz = mouseSpot.z - lastVoxelPosition.z;
extrudeScale = lastVoxelScale;
extrudeDirection = { x: 0, y: 0, z: 0 };
isExtruding = true;
if (dx > lastVoxelScale) extrudeDirection.x = extrudeScale;
else if (dx < -lastVoxelScale) extrudeDirection.x = -extrudeScale;
else if (dy > lastVoxelScale) extrudeDirection.y = extrudeScale;
else if (dy < -lastVoxelScale) extrudeDirection.y = -extrudeScale;
else if (dz > lastVoxelScale) extrudeDirection.z = extrudeScale;
else if (dz < -lastVoxelScale) extrudeDirection.z = -extrudeScale;
else isExtruding = false;
} else {
// We have got an extrusion direction, now look for mouse move beyond threshold to add new voxel
var dx = event.x - mouseX;
var dy = event.y - mouseY;
if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) {
lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection);
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z,
extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
mouseX = event.x;
mouseY = event.y;
}
}
}
// update the add voxel/delete voxel overlay preview
trackMouseEvent(event);
}
function mouseReleaseEvent(event) {
// if our tools are off, then don't do anything
if (!editToolsOn) {
return;
}
if (isMovingSlider) {
isMovingSlider = false;
}
if (isOrbiting) {
endOrbitMode(event);
isOrbiting = false;
}
if (isPanning) {
print("mouseReleaseEvent... calling endPanMode()");
endPanMode(event);
isPanning = false;
}
isAdding = false;
isExtruding = false;
}
function moveTools() {
// move the swatches
swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
swatchesY = windowDimensions.y - swatchHeight + 1;
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
// location so that it displays the "selected" marker
for (s = 0; s < numColors; s++) {
var extraWidth = 0;
if (s == 0) {
extraWidth = swatchExtraPadding;
}
var imageFromX = swatchExtraPadding - extraWidth + s * swatchWidth;
var imageFromY = swatchHeight + 1;
if (s == whichColor) {
imageFromY = 0;
}
var swatchX = swatchExtraPadding - extraWidth + swatchesX + ((swatchWidth - 1) * s);
if (s == (numColors - 1)) {
extraWidth = swatchExtraPadding;
}
Overlays.editOverlay(swatches[s], {
x: swatchX,
y: swatchesY,
subImage: { x: imageFromX, y: imageFromY, width: swatchWidth + extraWidth, height: swatchHeight },
color: colors[s],
alpha: 1,
visible: editToolsOn
});
}
// move the tools
toolsY = (windowDimensions.y - toolsHeight) / 2;
var voxelToolOffset = 1,
recolorToolOffset = 1,
eyedropperToolOffset = 1;
if (trackAsRecolor || recolorToolSelected) {
recolorToolOffset = 2;
} else if (trackAsEyedropper || eyedropperToolSelected) {
eyedropperToolOffset = 2;
} else if (trackAsOrbitOrPan) {
// nothing gets selected in this case...
} else {
voxelToolOffset = 2;
}
Overlays.editOverlay(voxelTool, {
subImage: { x: 0, y: toolHeight * voxelToolOffset, width: toolWidth, height: toolHeight },
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * voxelToolAt), width: toolWidth, height: toolHeight,
visible: editToolsOn
});
Overlays.editOverlay(recolorTool, {
subImage: { x: 0, y: toolHeight * recolorToolOffset, width: toolWidth, height: toolHeight },
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * recolorToolAt), width: toolWidth, height: toolHeight,
visible: editToolsOn
});
Overlays.editOverlay(eyedropperTool, {
subImage: { x: 0, y: toolHeight * eyedropperToolOffset, width: toolWidth, height: toolHeight },
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * eyedropperToolAt), width: toolWidth, height: toolHeight,
visible: editToolsOn
});
sliderX = swatchesX + swatchesWidth - sliderOffsetX;
sliderY = windowDimensions.y - sliderHeight + 1;
thumbY = sliderY + thumbOffsetY;
Overlays.editOverlay(slider, { x: sliderX, y: sliderY, visible: editToolsOn });
// This is the thumb of our slider
Overlays.editOverlay(thumb, { x: sliderX + thumbX, y: thumbY, visible: editToolsOn });
}
function touchBeginEvent(event) {
if (!editToolsOn) {
return;
}
// if we're already in the middle of orbiting or panning, then ignore these multi-touch events...
if (isOrbiting || isPanning) {
return;
}
if (event.isAlt || trackAsOrbitOrPan) {
if (event.touchPoints == touchPointsToOrbit) {
// we need to double check that we didn't start an orbit, because the touch events will sometimes
// come in as 2 then 3 touches...
if (isPanningFromTouch) {
print("touchBeginEvent... calling endPanMode()");
endPanMode(event);
isPanningFromTouch = false;
}
startOrbitMode(event);
isOrbitingFromTouch = true;
} else if (event.touchPoints == touchPointsToPan) {
// we need to double check that we didn't start an orbit, because the touch events will sometimes
// come in as 2 then 3 touches...
if (isOrbitingFromTouch) {
endOrbitMode(event);
isOrbitingFromTouch = false;
}
startPanMode(event);
isPanningFromTouch = true;
}
}
}
function touchUpdateEvent(event) {
if (!editToolsOn) {
return;
}
// if we're already in the middle of orbiting or panning, then ignore these multi-touch events...
if (isOrbiting || isPanning) {
return;
}
if (isOrbitingFromTouch) {
// we need to double check that we didn't start an orbit, because the touch events will sometimes
// come in as 2 then 3 touches...
if (event.touchPoints == touchPointsToPan) {
//print("we now have touchPointsToPan touches... switch to pan...");
endOrbitMode(event);
isOrbitingFromTouch = false;
startPanMode(event);
isPanningFromTouch = true;
} else {
handleOrbitingMove(event);
}
}
if (isPanningFromTouch) {
//print("touchUpdateEvent... isPanningFromTouch... event.touchPoints=" + event.touchPoints);
// we need to double check that we didn't start an orbit, because the touch events will sometimes
// come in as 2 then 3 touches...
if (event.touchPoints == touchPointsToOrbit) {
//print("we now have touchPointsToOrbit touches... switch to orbit...");
//print("touchUpdateEvent... calling endPanMode()");
endPanMode(event);
isPanningFromTouch = false;
startOrbitMode(event);
isOrbitingFromTouch = true;
handleOrbitingMove(event);
} else {
handlePanMove(event);
}
}
}
function touchEndEvent(event) {
if (!editToolsOn) {
return;
}
// if we're already in the middle of orbiting or panning, then ignore these multi-touch events...
if (isOrbiting || isPanning) {
return;
}
if (isOrbitingFromTouch) {
endOrbitMode(event);
isOrbitingFromTouch = false;
}
if (isPanningFromTouch) {
print("touchEndEvent... calling endPanMode()");
endPanMode(event);
isPanningFromTouch = false;
}
}
var lastFingerAddVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area
var lastFingerDeleteVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area
function checkControllers() {
var controllersPerPalm = 2; // palm and finger
for (var palm = 0; palm < 2; palm++) {
var palmController = palm * controllersPerPalm;
var fingerTipController = palmController + 1;
var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
var BUTTON_COUNT = 6;
var BUTTON_BASE = palm * BUTTON_COUNT;
var BUTTON_1 = BUTTON_BASE + 1;
var BUTTON_2 = BUTTON_BASE + 2;
var FINGERTIP_VOXEL_SIZE = 0.05;
if (Controller.isButtonPressed(BUTTON_1)) {
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerAddVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
if (whichColor == -1) {
newColor = { red: colors[0].red, green: colors[0].green, blue: colors[0].blue };
} else {
newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue };
}
Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE);
Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE,
newColor.red, newColor.green, newColor.blue);
lastFingerAddVoxel = fingerTipPosition;
}
} else if (Controller.isButtonPressed(BUTTON_2)) {
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerDeleteVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE);
lastFingerDeleteVoxel = fingerTipPosition;
}
}
}
}
function update(deltaTime) {
var newWindowDimensions = Controller.getViewportDimensions();
if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) {
windowDimensions = newWindowDimensions;
moveTools();
}
if (editToolsOn) {
checkControllers();
}
}
function wheelEvent(event) {
wheelPixelsMoved += event.delta;
if (Math.abs(wheelPixelsMoved) > WHEEL_PIXELS_PER_SCALE_CHANGE)
{
if (!pointerVoxelScaleSet) {
pointerVoxelScale = 1.0;
pointerVoxelScaleSet = true;
}
if (wheelPixelsMoved > 0) {
pointerVoxelScale /= 2.0;
if (pointerVoxelScale < MIN_VOXEL_SCALE) {
pointerVoxelScale = MIN_VOXEL_SCALE;
}
} else {
pointerVoxelScale *= 2.0;
if (pointerVoxelScale > MAX_VOXEL_SCALE) {
pointerVoxelScale = MAX_VOXEL_SCALE;
}
}
calcThumbFromScale(pointerVoxelScale);
trackMouseEvent(event);
wheelPixelsMoved = 0;
}
}
Controller.wheelEvent.connect(wheelEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Controller.touchBeginEvent.connect(touchBeginEvent);
Controller.touchUpdateEvent.connect(touchUpdateEvent);
Controller.touchEndEvent.connect(touchEndEvent);
Controller.captureKeyEvents({ text: "+" });
Controller.captureKeyEvents({ text: "-" });
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(voxelTool);
Overlays.deleteOverlay(recolorTool);
Overlays.deleteOverlay(eyedropperTool);
Overlays.deleteOverlay(slider);
Overlays.deleteOverlay(thumb);
Controller.releaseKeyEvents({ text: "+" });
Controller.releaseKeyEvents({ text: "-" });
cleanupMenus();
}
Script.scriptEnding.connect(scriptEnding);
Script.update.connect(update);
setupMenus();