overte/examples/editVoxels.js
2014-02-18 16:23:34 -08:00

546 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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 oldMode = Camera.getMode();
var key_alt = false;
var key_shift = false;
var isAdding = false;
var isExtruding = false;
var isOrbiting = false;
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 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 mouseX = 0;
var mouseY = 0;
// Create a table of the different colors you can choose
var colors = new Array();
colors[0] = { red: 237, green: 175, blue: 0 };
colors[1] = { red: 61, green: 211, blue: 72 };
colors[2] = { red: 51, green: 204, blue: 204 };
colors[3] = { red: 63, green: 169, blue: 245 };
colors[4] = { red: 193, green: 99, blue: 122 };
colors[5] = { red: 255, green: 54, blue: 69 };
colors[6] = { red: 124, green: 36, blue: 36 };
colors[7] = { red: 63, green: 35, blue: 19 };
var numColors = 8;
var whichColor = -1; // Starting color is 'Copy' mode
// Create sounds for adding, deleting, recoloring voxels
var addSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+create.raw");
var deleteSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+delete.raw");
var changeColorSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel+edit.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;
var editToolsOn = false; // starts out off
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
});
// 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 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
// location so that it displays the "selected" marker
for (s = 0; s < numColors; s++) {
var imageFromX = 12 + (s * 27);
var imageFromY = 0;
if (s == whichColor) {
imageFromY = 55;
}
var swatchX = swatchesX + (30 * s);
swatches[s] = Overlays.addOverlay("image", {
x: swatchX,
y: swatchesY,
width: swatchWidth,
height: swatchHeight,
subImage: { x: imageFromX, y: imageFromY, width: (swatchWidth - 1), height: swatchHeight },
imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg",
color: colors[s],
alpha: 1,
visible: editToolsOn
});
}
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;
function showPreviewVoxel() {
if (editToolsOn) {
var voxelColor;
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
var intersection = Voxels.findRayIntersection(pickRay);
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 = { 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 };
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: intersection.voxel.s + 0.002,
visible: true,
color: voxelColor,
solid: true,
alpha: 0.8
});
} 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;
}
Overlays.editOverlay(voxelPreview, {
position: guidePosition,
size: intersection.voxel.s,
visible: true,
color: voxelColor,
solid: true,
alpha: 0.7
});
} else if (isExtruding) {
Overlays.editOverlay(voxelPreview, { visible: false });
}
} else {
Overlays.editOverlay(voxelPreview, { visible: false });
}
}
function trackMouseEvent(event) {
trackLastMouseX = event.x;
trackLastMouseY = event.y;
trackAsDelete = event.isControl;
trackAsRecolor = event.isShifted;
showPreviewVoxel();
}
function trackKeyPressEvent(event) {
if (event.text == "CONTROL") {
trackAsDelete = true;
showPreviewVoxel();
}
if (event.text == "SHIFT") {
trackAsRecolor = true;
}
showPreviewVoxel();
}
function trackKeyReleaseEvent(event) {
if (event.text == "CONTROL") {
trackAsDelete = false;
showPreviewVoxel();
}
if (event.text == "SHIFT") {
trackAsRecolor = false;
}
// on TAB release, toggle our tool state
if (event.text == "TAB") {
editToolsOn = !editToolsOn;
moveTools();
Audio.playSound(clickSound, audioOptions);
}
showPreviewVoxel();
}
function mousePressEvent(event) {
// if our tools are off, then don't do anything
if (!editToolsOn) {
return;
}
if (event.isRightButton) {
// debugging of right button click on mac...
print(">>>> RIGHT BUTTON <<<<<");
}
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 (event.isAlt) {
// start orbit camera!
var cameraPosition = Camera.getPosition();
oldMode = Camera.getMode();
Camera.setMode("independent");
isOrbiting = true;
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));
} else if (trackAsDelete || event.isRightButton) {
// Delete voxel
Voxels.eraseVoxel(intersection.voxel.x, intersection.voxel.y, intersection.voxel.z, intersection.voxel.s);
Audio.playSound(deleteSound, audioOptions);
Overlays.editOverlay(voxelPreview, { visible: false });
} else if (trackAsRecolor) {
// Recolor Voxel
Voxels.setVoxel(intersection.voxel.x,
intersection.voxel.y,
intersection.voxel.z,
intersection.voxel.s,
colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue);
Audio.playSound(changeColorSound, audioOptions);
Overlays.editOverlay(voxelPreview, { visible: false });
} else {
// 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,
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,
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;
Audio.playSound(addSound, 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) {
key_alt = event.isAlt;
key_shift = event.isShifted;
var nVal = parseInt(event.text);
if (event.text == "0") {
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 == "9") {
// 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.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
setAudioPosition();
Audio.playSound(addSound, audioOptions);
}
}
// do this even if not in edit tools
if (event.text == " ") {
// Reset my orientation!
var orientation = { x:0, y:0, z:0, w:1 };
Camera.setOrientation(orientation);
MyAvatar.orientation = orientation;
}
trackKeyPressEvent(event); // used by preview support
}
function keyReleaseEvent(event) {
trackKeyReleaseEvent(event); // used by preview support
key_alt = false;
key_shift = false;
}
function mouseMoveEvent(event) {
if (isOrbiting) {
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;
}
if (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.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 (isOrbiting) {
var cameraOrientation = Camera.getOrientation();
var eulers = Quat.safeEulerAngles(cameraOrientation);
MyAvatar.position = Camera.getPosition();
MyAvatar.orientation = cameraOrientation;
Camera.stopLooking();
Camera.setMode(oldMode);
Camera.setOrientation(cameraOrientation);
}
isAdding = false;
isOrbiting = false;
isExtruding = false;
}
function moveTools() {
swatchesX = (windowDimensions.x - swatchesWidth) / 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
// location so that it displays the "selected" marker
for (s = 0; s < numColors; s++) {
var imageFromX = 12 + (s * 27);
var imageFromY = 0;
if (s == whichColor) {
imageFromY = 55;
}
var swatchX = swatchesX + ((swatchWidth - 1) * s);
Overlays.editOverlay(swatches[s], {
x: swatchX,
y: swatchesY,
subImage: { x: imageFromX, y: imageFromY, width: (swatchWidth - 1), height: swatchHeight },
color: colors[s],
alpha: 1,
visible: editToolsOn
});
}
}
function update() {
var newWindowDimensions = Controller.getViewportDimensions();
if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) {
windowDimensions = newWindowDimensions;
print("window resized...");
moveTools();
}
}
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
function scriptEnding() {
Overlays.deleteOverlay(voxelPreview);
for (s = 0; s < numColors; s++) {
Overlays.deleteOverlay(swatches[s]);
}
}
Script.scriptEnding.connect(scriptEnding);
Script.willSendVisualDataCallback.connect(update);