mirror of
https://github.com/lubosz/overte.git
synced 2025-04-14 11:06:15 +02:00
1518 lines
60 KiB
JavaScript
1518 lines
60 KiB
JavaScript
//
|
|
// editVoxels.js
|
|
// examples
|
|
//
|
|
// Created by Philip Rosedale on February 8, 2014
|
|
// Copyright 2014 High Fidelity, Inc.
|
|
//
|
|
// 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
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
Script.include("libraries/globals.js");
|
|
|
|
var editToolsOn = true; // starts out off
|
|
|
|
var windowDimensions = Controller.getViewportDimensions();
|
|
var WORLD_SCALE = Voxels.getTreeScale();
|
|
|
|
var NEW_VOXEL_SIZE = 1.0;
|
|
var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0;
|
|
var PIXELS_PER_EXTRUDE_VOXEL = 16;
|
|
var WHEEL_PIXELS_PER_SCALE_CHANGE = 100;
|
|
var MAX_VOXEL_SCALE_POWER = 4;
|
|
var MIN_VOXEL_SCALE_POWER = -8;
|
|
var MAX_VOXEL_SCALE = Math.pow(2.0, MAX_VOXEL_SCALE_POWER);
|
|
var MIN_VOXEL_SCALE = Math.pow(2.0, MIN_VOXEL_SCALE_POWER);
|
|
var WHITE_COLOR = { red: 255, green: 255, blue: 255 };
|
|
|
|
var MAX_PASTE_VOXEL_SCALE = 256;
|
|
var MIN_PASTE_VOXEL_SCALE = .256;
|
|
|
|
var zFightingSizeAdjustRatio = 0.004; // used to adjust preview voxels to prevent z fighting
|
|
var previewLineWidth = 1.5;
|
|
|
|
var inspectJsIsRunning = false;
|
|
var isAdding = false;
|
|
var isExtruding = false;
|
|
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;
|
|
|
|
// 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: 36, green: 64, blue: 64 };
|
|
var numColors = 9;
|
|
var whichColor = 0; // Starting color is 'Copy' mode
|
|
|
|
// Create sounds for for every script actions that require one
|
|
// start with audio slightly above the avatar
|
|
var audioOptions = {
|
|
position: Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0 } ),
|
|
volume: 1.0
|
|
};
|
|
|
|
function SoundArray() {
|
|
this.audioOptions = audioOptions
|
|
this.sounds = new Array();
|
|
this.addSound = function (soundURL) {
|
|
this.sounds[this.sounds.length] = SoundCache.getSound(soundURL);
|
|
}
|
|
this.play = function (index) {
|
|
if (0 <= index && index < this.sounds.length) {
|
|
Audio.playSound(this.sounds[index], this.audioOptions);
|
|
} else {
|
|
print("[ERROR] editVoxels.js:randSound.play() : Index " + index + " out of range.");
|
|
}
|
|
}
|
|
this.playRandom = function () {
|
|
if (this.sounds.length > 0) {
|
|
rand = Math.floor(Math.random() * this.sounds.length);
|
|
Audio.playSound(this.sounds[rand], this.audioOptions);
|
|
} else {
|
|
print("[ERROR] editVoxels.js:randSound.playRandom() : Array is empty.");
|
|
}
|
|
}
|
|
}
|
|
|
|
var addVoxelSound = new SoundArray();
|
|
addVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Add/VA+1.raw");
|
|
addVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Add/VA+2.raw");
|
|
addVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Add/VA+3.raw");
|
|
addVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Add/VA+4.raw");
|
|
addVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Add/VA+5.raw");
|
|
addVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Add/VA+6.raw");
|
|
|
|
var delVoxelSound = new SoundArray();
|
|
delVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Del/VD+A1.raw");
|
|
delVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Del/VD+A2.raw");
|
|
delVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Del/VD+A3.raw");
|
|
delVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Del/VD+B1.raw");
|
|
delVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Del/VD+B2.raw");
|
|
delVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Del/VD+B3.raw");
|
|
|
|
var resizeVoxelSound = new SoundArray();
|
|
resizeVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Size/V+Size+Minus.raw");
|
|
resizeVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Voxel+Size/V+Size+Plus.raw");
|
|
var voxelSizeMinus = 0;
|
|
var voxelSizePlus = 1;
|
|
|
|
var swatchesSound = new SoundArray();
|
|
swatchesSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Swatches/Swatch+1.raw");
|
|
swatchesSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Swatches/Swatch+2.raw");
|
|
swatchesSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Swatches/Swatch+3.raw");
|
|
swatchesSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Swatches/Swatch+4.raw");
|
|
swatchesSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Swatches/Swatch+5.raw");
|
|
swatchesSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Swatches/Swatch+6.raw");
|
|
swatchesSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Swatches/Swatch+7.raw");
|
|
swatchesSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Swatches/Swatch+8.raw");
|
|
swatchesSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Swatches/Swatch+9.raw");
|
|
|
|
var undoSound = new SoundArray();
|
|
undoSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Undo/Undo+1.raw");
|
|
undoSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Undo/Undo+2.raw");
|
|
undoSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Undo/Undo+3.raw");
|
|
|
|
var scriptInitSound = new SoundArray();
|
|
scriptInitSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Script+Init/Script+Init+A.raw");
|
|
scriptInitSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Script+Init/Script+Init+B.raw");
|
|
scriptInitSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Script+Init/Script+Init+C.raw");
|
|
scriptInitSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Script+Init/Script+Init+D.raw");
|
|
|
|
var modeSwitchSound = new SoundArray();
|
|
modeSwitchSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Mode+Switch/Mode+1.raw");
|
|
modeSwitchSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Mode+Switch/Mode+2.raw");
|
|
modeSwitchSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Mode+Switch/Mode+3.raw");
|
|
|
|
var initialVoxelSound = new SoundArray();
|
|
initialVoxelSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Initial+Voxel/Initial+V.raw");
|
|
|
|
var colorInheritSound = new SoundArray();
|
|
colorInheritSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Color+Inherit/Inherit+A.raw");
|
|
colorInheritSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Color+Inherit/Inherit+B.raw");
|
|
colorInheritSound.addSound(HIFI_PUBLIC_BUCKET + "sounds/Voxel+Editing/Color+Inherit/Inherit+C.raw");
|
|
|
|
// 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 = [];
|
|
var linePreviewBottom = [];
|
|
var linePreviewLeft = [];
|
|
var linePreviewRight = [];
|
|
|
|
// Currend cursor index
|
|
var currentCursor = 0;
|
|
|
|
function addLineOverlay() {
|
|
return Overlays.addOverlay("line3d", {
|
|
start: { 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
|
|
});
|
|
}
|
|
|
|
//Cursor line previews for up to three cursors
|
|
linePreviewTop[0] = addLineOverlay();
|
|
linePreviewTop[1] = addLineOverlay();
|
|
linePreviewTop[2] = addLineOverlay();
|
|
|
|
linePreviewBottom[0] = addLineOverlay();
|
|
linePreviewBottom[1] = addLineOverlay();
|
|
linePreviewBottom[2] = addLineOverlay();
|
|
|
|
linePreviewLeft[0] = addLineOverlay();
|
|
linePreviewLeft[1] = addLineOverlay();
|
|
linePreviewLeft[2] = addLineOverlay();
|
|
|
|
linePreviewRight[0] = addLineOverlay();
|
|
linePreviewRight[1] = addLineOverlay();
|
|
linePreviewRight[2] = addLineOverlay();
|
|
|
|
// these will be used below
|
|
var scaleSelectorWidth = 144;
|
|
var scaleSelectorHeight = 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 + scaleSelectorWidth)) / 2;
|
|
var swatchesY = windowDimensions.y - swatchHeight + 1;
|
|
|
|
var toolIconUrl = HIFI_PUBLIC_BUCKET + "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 pasteModeColor = { red: 132, green: 61, blue: 255 };
|
|
|
|
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: editToolsOn,
|
|
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: editToolsOn,
|
|
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: editToolsOn,
|
|
alpha: 0.9
|
|
});
|
|
|
|
|
|
var copyScale = true;
|
|
function ScaleSelector() {
|
|
this.x = swatchesX + swatchesWidth;
|
|
this.y = swatchesY;
|
|
this.width = scaleSelectorWidth;
|
|
this.height = scaleSelectorHeight;
|
|
|
|
this.displayPower = false;
|
|
this.scale = 1.0;
|
|
this.power = 0;
|
|
|
|
this.FIRST_PART = this.width * 40.0 / 100.0;
|
|
this.SECOND_PART = this.width * 37.0 / 100.0;
|
|
|
|
this.buttonsOverlay = Overlays.addOverlay("image", {
|
|
x: this.x, y: this.y,
|
|
width: this.width, height: this.height,
|
|
//subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
|
|
imageURL: toolIconUrl + "voxel-size-selector.svg",
|
|
alpha: 0.9,
|
|
visible: editToolsOn
|
|
});
|
|
this.textOverlay = Overlays.addOverlay("text", {
|
|
x: this.x + this.FIRST_PART, y: this.y,
|
|
width: this.SECOND_PART, height: this.height,
|
|
topMargin: 13,
|
|
text: this.scale.toString(),
|
|
backgroundAlpha: 0.0,
|
|
visible: editToolsOn
|
|
});
|
|
this.powerOverlay = Overlays.addOverlay("text", {
|
|
x: this.x + this.FIRST_PART, y: this.y,
|
|
width: this.SECOND_PART, height: this.height,
|
|
leftMargin: 28,
|
|
text: this.power.toString(),
|
|
backgroundAlpha: 0.0,
|
|
visible: false
|
|
});
|
|
this.setScale = function(scale) {
|
|
if (scale > MAX_VOXEL_SCALE) {
|
|
scale = MAX_VOXEL_SCALE;
|
|
}
|
|
if (scale < MIN_VOXEL_SCALE) {
|
|
scale = MIN_VOXEL_SCALE;
|
|
}
|
|
|
|
this.scale = scale;
|
|
this.power = Math.floor(Math.log(scale) / Math.log(2));
|
|
rescaleImport();
|
|
this.update();
|
|
}
|
|
|
|
this.show = function(doShow) {
|
|
Overlays.editOverlay(this.buttonsOverlay, {visible: doShow});
|
|
Overlays.editOverlay(this.textOverlay, {visible: doShow});
|
|
Overlays.editOverlay(this.powerOverlay, {visible: doShow && this.displayPower});
|
|
}
|
|
|
|
this.move = function() {
|
|
this.x = swatchesX + swatchesWidth;
|
|
this.y = swatchesY;
|
|
|
|
Overlays.editOverlay(this.buttonsOverlay, {
|
|
x: this.x, y: this.y,
|
|
});
|
|
Overlays.editOverlay(this.textOverlay, {
|
|
x: this.x + this.FIRST_PART, y: this.y,
|
|
});
|
|
Overlays.editOverlay(this.powerOverlay, {
|
|
x: this.x + this.FIRST_PART, y: this.y,
|
|
});
|
|
}
|
|
|
|
|
|
this.switchDisplay = function() {
|
|
this.displayPower = !this.displayPower;
|
|
|
|
if (this.displayPower) {
|
|
Overlays.editOverlay(this.textOverlay, {
|
|
leftMargin: 18,
|
|
text: "2"
|
|
});
|
|
Overlays.editOverlay(this.powerOverlay, {
|
|
text: this.power.toString(),
|
|
visible: editToolsOn
|
|
});
|
|
} else {
|
|
Overlays.editOverlay(this.textOverlay, {
|
|
leftMargin: 13,
|
|
text: this.scale.toString()
|
|
});
|
|
Overlays.editOverlay(this.powerOverlay, {
|
|
visible: false
|
|
});
|
|
}
|
|
}
|
|
|
|
this.update = function() {
|
|
if (this.displayPower) {
|
|
Overlays.editOverlay(this.powerOverlay, {text: this.power.toString()});
|
|
} else {
|
|
Overlays.editOverlay(this.textOverlay, {text: this.scale.toString()});
|
|
}
|
|
}
|
|
|
|
this.incrementScale = function() {
|
|
copyScale = false;
|
|
if (this.power < MAX_VOXEL_SCALE_POWER) {
|
|
++this.power;
|
|
this.scale *= 2.0;
|
|
this.update();
|
|
rescaleImport();
|
|
resizeVoxelSound.play(voxelSizePlus);
|
|
}
|
|
}
|
|
|
|
this.decrementScale = function() {
|
|
copyScale = false;
|
|
if (MIN_VOXEL_SCALE_POWER < this.power) {
|
|
--this.power;
|
|
this.scale /= 2.0;
|
|
this.update();
|
|
rescaleImport();
|
|
resizeVoxelSound.play(voxelSizePlus);
|
|
}
|
|
}
|
|
|
|
this.clicked = function(x, y) {
|
|
if (this.x < x && x < this.x + this.width &&
|
|
this.y < y && y < this.y + this.height) {
|
|
|
|
if (x < this.x + this.FIRST_PART) {
|
|
this.decrementScale();
|
|
} else if (x < this.x + this.FIRST_PART + this.SECOND_PART) {
|
|
this.switchDisplay();
|
|
} else {
|
|
this.incrementScale();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
this.cleanup = function() {
|
|
Overlays.deleteOverlay(this.buttonsOverlay);
|
|
Overlays.deleteOverlay(this.textOverlay);
|
|
Overlays.deleteOverlay(this.powerOverlay);
|
|
}
|
|
|
|
}
|
|
var scaleSelector = new ScaleSelector();
|
|
|
|
|
|
///////////////////////////////////// IMPORT MODULE ///////////////////////////////
|
|
// Move the following code to a separate file when include will be available.
|
|
var importTree;
|
|
var importPreview;
|
|
var importBoundaries;
|
|
var xImportGuide;
|
|
var yImportGuide;
|
|
var zImportGuide;
|
|
var isImporting;
|
|
var importPosition;
|
|
var importDistance;
|
|
|
|
function initImport() {
|
|
importPreview = Overlays.addOverlay("localvoxels", {
|
|
name: "import",
|
|
position: { x: 0, y: 0, z: 0},
|
|
scale: 1,
|
|
visible: false
|
|
});
|
|
importBoundaries = Overlays.addOverlay("cube", {
|
|
position: { x: 0, y: 0, z: 0 },
|
|
size: 1,
|
|
color: { red: 128, blue: 128, green: 128 },
|
|
lineWIdth: 4,
|
|
solid: false,
|
|
visible: false
|
|
});
|
|
|
|
xImportGuide = Overlays.addOverlay("line3d", {
|
|
start: { x: 0, y: 0, z: 0},
|
|
end: { x: 0, y: 0, z: 0},
|
|
color: { red: 255, green: 0, blue: 0},
|
|
alpha: 1,
|
|
visible: false,
|
|
lineWidth: previewLineWidth
|
|
});
|
|
yImportGuide = Overlays.addOverlay("line3d", {
|
|
start: { x: 0, y: 0, z: 0},
|
|
end: { x: 0, y: 0, z: 0},
|
|
color: { red: 0, green: 255, blue: 0},
|
|
alpha: 1,
|
|
visible: false,
|
|
lineWidth: previewLineWidth
|
|
});
|
|
zImportGuide = Overlays.addOverlay("line3d", {
|
|
start: { x: 0, y: 0, z: 0},
|
|
end: { x: 0, y: 0, z: 0},
|
|
color: { red: 0, green: 0, blue: 255},
|
|
alpha: 1,
|
|
visible: false,
|
|
lineWidth: previewLineWidth
|
|
});
|
|
|
|
|
|
isImporting = false;
|
|
importPosition = { x: 0, y: 0, z: 0 };
|
|
}
|
|
|
|
function importVoxels() {
|
|
isImporting = Clipboard.importVoxels();
|
|
return isImporting;
|
|
}
|
|
|
|
function moveImport(position) {
|
|
importPosition = position;
|
|
Overlays.editOverlay(importPreview, {
|
|
position: { x: importPosition.x, y: importPosition.y, z: importPosition.z }
|
|
});
|
|
Overlays.editOverlay(importBoundaries, {
|
|
position: { x: importPosition.x, y: importPosition.y, z: importPosition.z }
|
|
});
|
|
|
|
|
|
Overlays.editOverlay(xImportGuide, {
|
|
start: { x: importPosition.x, y: 0, z: importPosition.z },
|
|
end: { x: importPosition.x + scaleSelector.scale, y: 0, z: importPosition.z }
|
|
});
|
|
Overlays.editOverlay(yImportGuide, {
|
|
start: { x: importPosition.x, y: importPosition.y, z: importPosition.z },
|
|
end: { x: importPosition.x, y: 0, z: importPosition.z }
|
|
});
|
|
Overlays.editOverlay(zImportGuide, {
|
|
start: { x: importPosition.x, y: 0, z: importPosition.z },
|
|
end: { x: importPosition.x, y: 0, z: importPosition.z + scaleSelector.scale }
|
|
});
|
|
rescaleImport();
|
|
}
|
|
|
|
function rescaleImport() {
|
|
if (0 < scaleSelector.scale) {
|
|
Overlays.editOverlay(importPreview, {
|
|
scale: scaleSelector.scale
|
|
});
|
|
Overlays.editOverlay(importBoundaries, {
|
|
size: scaleSelector.scale
|
|
});
|
|
|
|
Overlays.editOverlay(xImportGuide, {
|
|
end: { x: importPosition.x + scaleSelector.scale, y: 0, z: importPosition.z }
|
|
});
|
|
Overlays.editOverlay(yImportGuide, {
|
|
end: { x: importPosition.x, y: 0, z: importPosition.z }
|
|
});
|
|
Overlays.editOverlay(zImportGuide, {
|
|
end: { x: importPosition.x, y: 0, z: importPosition.z + scaleSelector.scale }
|
|
});
|
|
}
|
|
}
|
|
|
|
function showImport(doShow) {
|
|
Overlays.editOverlay(importPreview, {
|
|
visible: doShow
|
|
});
|
|
Overlays.editOverlay(importBoundaries, {
|
|
visible: doShow
|
|
});
|
|
|
|
Overlays.editOverlay(xImportGuide, {
|
|
visible: doShow
|
|
});
|
|
Overlays.editOverlay(yImportGuide, {
|
|
visible: doShow
|
|
});
|
|
Overlays.editOverlay(zImportGuide, {
|
|
visible: doShow
|
|
});
|
|
}
|
|
|
|
function placeImport() {
|
|
if (isImporting) {
|
|
Clipboard.pasteVoxel(importPosition.x, importPosition.y, importPosition.z, scaleSelector.scale);
|
|
isImporting = false;
|
|
}
|
|
}
|
|
|
|
function cancelImport() {
|
|
if (isImporting) {
|
|
isImporting = false;
|
|
showImport(false);
|
|
}
|
|
}
|
|
|
|
function cleanupImport() {
|
|
Overlays.deleteOverlay(importPreview);
|
|
Overlays.deleteOverlay(importBoundaries);
|
|
Overlays.deleteOverlay(xImportGuide);
|
|
Overlays.deleteOverlay(yImportGuide);
|
|
Overlays.deleteOverlay(zImportGuide);
|
|
isImporting = false;
|
|
importPostion = { x: 0, y: 0, z: 0 };
|
|
}
|
|
/////////////////////////////////// END IMPORT MODULE /////////////////////////////
|
|
initImport();
|
|
|
|
if (editToolsOn) {
|
|
moveTools();
|
|
}
|
|
|
|
function setAudioPosition() {
|
|
var position = MyAvatar.position;
|
|
var forwardVector = Quat.getFront(MyAvatar.orientation);
|
|
audioOptions.position = Vec3.sum(position, forwardVector);
|
|
}
|
|
|
|
function getNewPasteVoxel(pickRay) {
|
|
|
|
var voxelSize = scaleSelector.scale;
|
|
var origin = { x: pickRay.direction.x, y: pickRay.direction.y, z: pickRay.direction.z };
|
|
|
|
origin.x += pickRay.origin.x;
|
|
origin.y += pickRay.origin.y;
|
|
origin.z += pickRay.origin.z;
|
|
|
|
origin.x -= voxelSize / 2;
|
|
origin.y -= voxelSize / 2;
|
|
origin.z += voxelSize / 2;
|
|
|
|
return {origin: origin, voxelSize: voxelSize};
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
var trackLastMouseX = 0;
|
|
var trackLastMouseY = 0;
|
|
var trackAsDelete = false;
|
|
var trackAsRecolor = false;
|
|
var trackAsEyedropper = false;
|
|
|
|
var voxelToolSelected = false;
|
|
var recolorToolSelected = false;
|
|
var eyedropperToolSelected = false;
|
|
var pasteMode = false;
|
|
|
|
|
|
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 = scaleSelector.scale;
|
|
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);
|
|
}
|
|
|
|
var zFightingSizeAdjust = zFightingSizeAdjustRatio * intersection.distance;
|
|
|
|
// 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;
|
|
highlightAt.y = y + zFightingSizeAdjust;
|
|
highlightAt.z = z + zFightingSizeAdjust;
|
|
voxelSize -= 2 * zFightingSizeAdjust;
|
|
if (wantAddAdjust) {
|
|
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 = x + voxelSize + zFightingSizeAdjust;
|
|
highlightAt.y = y + zFightingSizeAdjust;
|
|
highlightAt.z = z + zFightingSizeAdjust;
|
|
voxelSize -= 2 * zFightingSizeAdjust;
|
|
if (wantAddAdjust) {
|
|
resultVoxel.x += resultVoxel.s;
|
|
}
|
|
|
|
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
|
|
resultVoxel.bottomLeft = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize };
|
|
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
|
|
resultVoxel.topLeft = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z + voxelSize };
|
|
|
|
} else if (intersection.face == "MIN_Y_FACE") {
|
|
|
|
highlightAt.x = x + zFightingSizeAdjust;
|
|
highlightAt.y = y - zFightingSizeAdjust;
|
|
highlightAt.z = z + zFightingSizeAdjust;
|
|
voxelSize -= 2 * zFightingSizeAdjust;
|
|
if (wantAddAdjust) {
|
|
resultVoxel.y -= voxelSize;
|
|
}
|
|
|
|
resultVoxel.topRight = {x: highlightAt.x , y: highlightAt.y, z: highlightAt.z };
|
|
resultVoxel.topLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y, z: highlightAt.z };
|
|
resultVoxel.bottomRight = {x: highlightAt.x , y: highlightAt.y, z: highlightAt.z + voxelSize };
|
|
resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize , y: highlightAt.y, z: highlightAt.z + voxelSize };
|
|
|
|
} else if (intersection.face == "MAX_Y_FACE") {
|
|
|
|
highlightAt.x = x + zFightingSizeAdjust;
|
|
highlightAt.y = y + voxelSize + zFightingSizeAdjust;
|
|
highlightAt.z = z + zFightingSizeAdjust;
|
|
voxelSize -= 2 * zFightingSizeAdjust;
|
|
if (wantAddAdjust) {
|
|
resultVoxel.y += resultVoxel.s;
|
|
}
|
|
|
|
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
|
|
resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y, z: highlightAt.z};
|
|
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z + voxelSize};
|
|
resultVoxel.topLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y, z: highlightAt.z + voxelSize};
|
|
|
|
} else if (intersection.face == "MIN_Z_FACE") {
|
|
|
|
highlightAt.x = x + zFightingSizeAdjust;
|
|
highlightAt.y = y + zFightingSizeAdjust;
|
|
highlightAt.z = z - zFightingSizeAdjust;
|
|
voxelSize -= 2 * zFightingSizeAdjust;
|
|
if (wantAddAdjust) {
|
|
resultVoxel.z -= voxelSize;
|
|
}
|
|
|
|
resultVoxel.bottomRight = {x: highlightAt.x, y: highlightAt.y, z: highlightAt.z };
|
|
resultVoxel.bottomLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y, z: highlightAt.z};
|
|
resultVoxel.topRight = {x: highlightAt.x, y: highlightAt.y + voxelSize, z: highlightAt.z };
|
|
resultVoxel.topLeft = {x: highlightAt.x + voxelSize, y: highlightAt.y + voxelSize, z: highlightAt.z};
|
|
|
|
} else if (intersection.face == "MAX_Z_FACE") {
|
|
|
|
highlightAt.x = x + zFightingSizeAdjust;
|
|
highlightAt.y = y + zFightingSizeAdjust;
|
|
highlightAt.z = z + voxelSize + zFightingSizeAdjust;
|
|
voxelSize -= 2 * zFightingSizeAdjust;
|
|
if (wantAddAdjust) {
|
|
resultVoxel.z += resultVoxel.s;
|
|
}
|
|
|
|
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);
|
|
|
|
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 (voxelToolSelected && !isExtruding) {
|
|
Overlays.editOverlay(voxelPreview, { visible: true });
|
|
} else if (isExtruding) {
|
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
|
}
|
|
}
|
|
|
|
function showPreviewLines() {
|
|
|
|
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
|
|
|
|
if (pasteMode) { // free voxel pasting
|
|
|
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
|
Overlays.editOverlay(linePreviewLeft, { visible: false });
|
|
|
|
var pasteVoxel = getNewPasteVoxel(pickRay);
|
|
|
|
// X axis
|
|
Overlays.editOverlay(linePreviewBottom[currentCursor], {
|
|
position: pasteVoxel.origin,
|
|
end: {x: pasteVoxel.origin.x + pasteVoxel.voxelSize, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z },
|
|
visible: true
|
|
});
|
|
|
|
// Y axis
|
|
Overlays.editOverlay(linePreviewRight[currentCursor], {
|
|
position: pasteVoxel.origin,
|
|
end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y + pasteVoxel.voxelSize, z: pasteVoxel.origin.z },
|
|
visible: true
|
|
});
|
|
|
|
// Z axis
|
|
Overlays.editOverlay(linePreviewTop[currentCursor], {
|
|
position: pasteVoxel.origin,
|
|
end: {x: pasteVoxel.origin.x, y: pasteVoxel.origin.y, z: pasteVoxel.origin.z - pasteVoxel.voxelSize },
|
|
visible: true
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
var intersection = Voxels.findRayIntersection(pickRay);
|
|
|
|
if (intersection.intersects) {
|
|
resultVoxel = calculateVoxelFromIntersection(intersection,"");
|
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
|
Overlays.editOverlay(linePreviewTop[currentCursor], { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true });
|
|
Overlays.editOverlay(linePreviewBottom[currentCursor], { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true });
|
|
Overlays.editOverlay(linePreviewLeft[currentCursor], { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true });
|
|
Overlays.editOverlay(linePreviewRight[currentCursor], { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true });
|
|
colors[0] = {red: intersection.voxel.red, green: intersection.voxel.green , blue: intersection.voxel.blue };
|
|
|
|
if (copyScale) {
|
|
scaleSelector.setScale(intersection.voxel.s);
|
|
}
|
|
moveTools();
|
|
} else if (intersection.accurate) {
|
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
|
Overlays.editOverlay(linePreviewTop[currentCursor], { visible: false });
|
|
Overlays.editOverlay(linePreviewBottom[currentCursor], { visible: false });
|
|
Overlays.editOverlay(linePreviewLeft[currentCursor], { visible: false });
|
|
Overlays.editOverlay(linePreviewRight[currentCursor], { visible: false });
|
|
}
|
|
}
|
|
|
|
function showPreviewGuides() {
|
|
if (editToolsOn && !isImporting && (voxelToolSelected || recolorToolSelected || eyedropperToolSelected)) {
|
|
if (previewAsVoxel) {
|
|
showPreviewVoxel();
|
|
|
|
// make sure alternative is hidden
|
|
Overlays.editOverlay(linePreviewTop[currentCursor], { visible: false });
|
|
Overlays.editOverlay(linePreviewBottom[currentCursor], { visible: false });
|
|
Overlays.editOverlay(linePreviewLeft[currentCursor], { visible: false });
|
|
Overlays.editOverlay(linePreviewRight[currentCursor], { visible: false });
|
|
} else {
|
|
showPreviewLines();
|
|
}
|
|
} else {
|
|
// make sure all previews are off
|
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
|
Overlays.editOverlay(linePreviewTop[currentCursor], { visible: false });
|
|
Overlays.editOverlay(linePreviewBottom[currentCursor], { visible: false });
|
|
Overlays.editOverlay(linePreviewLeft[currentCursor], { visible: false });
|
|
Overlays.editOverlay(linePreviewRight[currentCursor], { visible: false });
|
|
}
|
|
}
|
|
|
|
function trackMouseEvent(event) {
|
|
trackLastMouseX = event.x;
|
|
trackLastMouseY = event.y;
|
|
trackAsDelete = event.isControl;
|
|
trackAsRecolor = event.isShifted;
|
|
trackAsEyedropper = event.isMeta;
|
|
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") {
|
|
inspectJsIsRunning = true;
|
|
}
|
|
showPreviewGuides();
|
|
}
|
|
|
|
function trackKeyReleaseEvent(event) {
|
|
// on TAB release, toggle our tool state
|
|
if (event.text == "TAB") {
|
|
editToolsOn = !editToolsOn;
|
|
moveTools();
|
|
setAudioPosition(); // make sure we set the audio position before playing sounds
|
|
showPreviewGuides();
|
|
scaleSelector.show(editToolsOn);
|
|
scriptInitSound.playRandom();
|
|
}
|
|
|
|
if (event.text == "ALT") {
|
|
inspectJsIsRunning = false;
|
|
}
|
|
|
|
if (editToolsOn) {
|
|
if (event.text == "ESC") {
|
|
pasteMode = false;
|
|
moveTools();
|
|
}
|
|
if (event.text == "-") {
|
|
scaleSelector.decrementScale();
|
|
}
|
|
if (event.text == "+") {
|
|
scaleSelector.incrementScale();
|
|
}
|
|
if (event.text == "CONTROL") {
|
|
trackAsDelete = false;
|
|
moveTools();
|
|
}
|
|
if (event.text == "SHIFT") {
|
|
trackAsRecolor = false;
|
|
moveTools();
|
|
}
|
|
if (event.text == "META") {
|
|
trackAsEyedropper = false;
|
|
moveTools();
|
|
}
|
|
|
|
// on F1 toggle the preview mode between cubes and lines
|
|
if (event.text == "F1") {
|
|
previewAsVoxel = !previewAsVoxel;
|
|
}
|
|
|
|
showPreviewGuides();
|
|
}
|
|
}
|
|
|
|
function mousePressEvent(event) {
|
|
// if our tools are off, then don't do anything
|
|
if (!editToolsOn) {
|
|
return;
|
|
}
|
|
if (inspectJsIsRunning) {
|
|
return;
|
|
}
|
|
|
|
if (event.deviceID == 1500) { // Left Hydra Controller
|
|
currentCursor = 0;
|
|
} else if (event.deviceID == 1501) { // Right Hydra Controller
|
|
currentCursor = 1;
|
|
} else {
|
|
currentCursor = 2;
|
|
}
|
|
|
|
var clickedOnSomething = false;
|
|
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
|
|
|
if (clickedOverlay == voxelTool) {
|
|
modeSwitchSound.play(0);
|
|
voxelToolSelected = !voxelToolSelected;
|
|
recolorToolSelected = false;
|
|
eyedropperToolSelected = false;
|
|
moveTools();
|
|
clickedOnSomething = true;
|
|
} else if (clickedOverlay == recolorTool) {
|
|
modeSwitchSound.play(1);
|
|
voxelToolSelected = false;
|
|
recolorToolSelected = !recolorToolSelected;
|
|
eyedropperToolSelected = false;
|
|
moveTools();
|
|
clickedOnSomething = true;
|
|
} else if (clickedOverlay == eyedropperTool) {
|
|
modeSwitchSound.play(2);
|
|
voxelToolSelected = false;
|
|
recolorToolSelected = false;
|
|
eyedropperToolSelected = !eyedropperToolSelected;
|
|
moveTools();
|
|
clickedOnSomething = true;
|
|
} else if (scaleSelector.clicked(event.x, event.y)) {
|
|
if (isImporting) {
|
|
rescaleImport();
|
|
}
|
|
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;
|
|
swatchesSound.play(whichColor);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (clickedOnSomething || isImporting || (!voxelToolSelected && !recolorToolSelected && !eyedropperToolSelected)) {
|
|
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
|
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
|
var intersection = Voxels.findRayIntersection(pickRay);
|
|
audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction);
|
|
|
|
if (pasteMode) {
|
|
var pasteVoxel = getNewPasteVoxel(pickRay);
|
|
Clipboard.pasteVoxel(pasteVoxel.origin.x, pasteVoxel.origin.y, pasteVoxel.origin.z, pasteVoxel.voxelSize);
|
|
pasteMode = false;
|
|
moveTools();
|
|
return;
|
|
}
|
|
|
|
if (intersection.intersects) {
|
|
if (trackAsDelete || event.isRightButton && !trackAsEyedropper) {
|
|
// Delete voxel
|
|
voxelDetails = calculateVoxelFromIntersection(intersection,"delete");
|
|
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
|
|
delVoxelSound.playRandom();
|
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
|
} else if (eyedropperToolSelected || trackAsEyedropper) {
|
|
colors[whichColor].red = intersection.voxel.red;
|
|
colors[whichColor].green = intersection.voxel.green;
|
|
colors[whichColor].blue = intersection.voxel.blue;
|
|
moveTools();
|
|
swatchesSound.play(whichColor);
|
|
|
|
} else if (recolorToolSelected || trackAsRecolor) {
|
|
// Recolor Voxel
|
|
voxelDetails = calculateVoxelFromIntersection(intersection,"recolor");
|
|
|
|
// doing this erase then set will make sure we only recolor just the target voxel
|
|
Voxels.setVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s,
|
|
colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue);
|
|
swatchesSound.play(whichColor);
|
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
|
} else if (voxelToolSelected) {
|
|
// Add voxel on face
|
|
newColor = { red: colors[whichColor].red,
|
|
green: colors[whichColor].green,
|
|
blue: colors[whichColor].blue
|
|
};
|
|
|
|
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;
|
|
if (lastVoxelScale > MAX_VOXEL_SCALE) {
|
|
lastVoxelScale = MAX_VOXEL_SCALE;
|
|
}
|
|
|
|
addVoxelSound.playRandom();
|
|
|
|
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 == "`") {
|
|
copyScale = true;
|
|
} else if ((nVal > 0) && (nVal <= numColors)) {
|
|
whichColor = nVal - 1;
|
|
print("Color = " + (whichColor + 1));
|
|
swatchesSound.play(whichColor);
|
|
moveTools();
|
|
} else if (event.text == "0" && voxelToolSelected) {
|
|
// Create a brand new 1 meter voxel in front of your avatar
|
|
var newPosition = getNewVoxelPosition();
|
|
var newVoxel = {
|
|
x: newPosition.x,
|
|
y: newPosition.y ,
|
|
z: newPosition.z,
|
|
s: NEW_VOXEL_SIZE,
|
|
red: colors[whichColor].red,
|
|
green: colors[whichColor].green,
|
|
blue: colors[whichColor].blue };
|
|
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
|
setAudioPosition();
|
|
initialVoxelSound.playRandom();
|
|
} else if (event.text == "z") {
|
|
undoSound.playRandom();
|
|
}
|
|
}
|
|
|
|
trackKeyPressEvent(event); // used by preview support
|
|
}
|
|
|
|
function keyReleaseEvent(event) {
|
|
trackKeyReleaseEvent(event); // used by preview support
|
|
}
|
|
|
|
|
|
// In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already
|
|
// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that
|
|
// added it.
|
|
var voxelMenuAddedDelete = false;
|
|
function setupVoxelMenus() {
|
|
// 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" });
|
|
|
|
|
|
if (!Menu.menuItemExists("Edit","Delete")) {
|
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete",
|
|
shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" });
|
|
voxelMenuAddedDelete = true;
|
|
}
|
|
|
|
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");
|
|
if (voxelMenuAddedDelete) {
|
|
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 (editToolsOn) {
|
|
|
|
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);
|
|
pasteMode = true;
|
|
moveTools();
|
|
}
|
|
if (menuItem == "Cut") {
|
|
print("cutting...");
|
|
Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
|
pasteMode = true;
|
|
moveTools();
|
|
}
|
|
if (menuItem == "Paste") {
|
|
if (isImporting) {
|
|
print("placing import...");
|
|
placeImport();
|
|
showImport(false);
|
|
} else {
|
|
print("pasting...");
|
|
Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
|
}
|
|
pasteMode = false;
|
|
moveTools();
|
|
}
|
|
if (menuItem == "Delete") {
|
|
print("deleting...");
|
|
if (isImporting) {
|
|
cancelImport();
|
|
} else if (voxelToolSelected) {
|
|
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("importing...");
|
|
if (importVoxels()) {
|
|
showImport(true);
|
|
}
|
|
moveTools();
|
|
}
|
|
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 || inspectJsIsRunning) {
|
|
return;
|
|
}
|
|
|
|
if (event.deviceID == 1500) { // Left Hydra Controller
|
|
currentCursor = 0;
|
|
} else if (event.deviceID == 1501) { // Right Hydra Controller
|
|
currentCursor = 1;
|
|
} else {
|
|
currentCursor = 2;
|
|
}
|
|
|
|
// Move Import Preview
|
|
if (isImporting) {
|
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
|
var intersection = Voxels.findRayIntersection(pickRay);
|
|
|
|
var distance = 2 * scaleSelector.scale;
|
|
|
|
if (intersection.intersects) {
|
|
var intersectionDistance = Vec3.length(Vec3.subtract(pickRay.origin, intersection.intersection));
|
|
if (intersectionDistance < distance) {
|
|
distance = intersectionDistance * 0.99;
|
|
}
|
|
|
|
}
|
|
|
|
var targetPosition = { x: pickRay.direction.x * distance,
|
|
y: pickRay.direction.y * distance,
|
|
z: pickRay.direction.z * distance
|
|
};
|
|
targetPosition.x += pickRay.origin.x;
|
|
targetPosition.y += pickRay.origin.y;
|
|
targetPosition.z += pickRay.origin.z;
|
|
|
|
if (targetPosition.x < 0) targetPosition.x = 0;
|
|
if (targetPosition.y < 0) targetPosition.y = 0;
|
|
if (targetPosition.z < 0) targetPosition.z = 0;
|
|
|
|
var nudgeFactor = scaleSelector.scale;
|
|
var newPosition = {
|
|
x: Math.floor(targetPosition.x / nudgeFactor) * nudgeFactor,
|
|
y: Math.floor(targetPosition.y / nudgeFactor) * nudgeFactor,
|
|
z: Math.floor(targetPosition.z / nudgeFactor) * nudgeFactor
|
|
}
|
|
|
|
moveImport(newPosition);
|
|
}
|
|
|
|
if (isAdding) {
|
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
|
var distance = Vec3.length(Vec3.subtract(pickRay.origin, lastVoxelPosition));
|
|
var mouseSpot = Vec3.sum(Vec3.multiply(pickRay.direction, distance), pickRay.origin);
|
|
var delta = Vec3.subtract(mouseSpot, lastVoxelPosition);
|
|
|
|
if (!isExtruding) {
|
|
// Use the drag direction to tell which way to 'extrude' this voxel
|
|
extrudeScale = lastVoxelScale;
|
|
extrudeDirection = { x: 0, y: 0, z: 0 };
|
|
isExtruding = true;
|
|
if (delta.x > lastVoxelScale) extrudeDirection.x = 1;
|
|
else if (delta.x < -lastVoxelScale) extrudeDirection.x = -1;
|
|
else if (delta.y > lastVoxelScale) extrudeDirection.y = 1;
|
|
else if (delta.y < -lastVoxelScale) extrudeDirection.y = -1;
|
|
else if (delta.z > lastVoxelScale) extrudeDirection.z = 1;
|
|
else if (delta.z < -lastVoxelScale) extrudeDirection.z = -1;
|
|
else isExtruding = false;
|
|
} else {
|
|
// Extrude if mouse has moved by a voxel in the extrude direction
|
|
var distanceInDirection = Vec3.dot(delta, extrudeDirection);
|
|
if (distanceInDirection > extrudeScale) {
|
|
lastVoxelPosition = Vec3.sum(lastVoxelPosition, Vec3.multiply(extrudeDirection, extrudeScale));
|
|
Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, extrudeScale,
|
|
lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 (inspectJsIsRunning) {
|
|
return;
|
|
}
|
|
|
|
isAdding = false;
|
|
isExtruding = false;
|
|
}
|
|
|
|
function moveTools() {
|
|
// move the swatches
|
|
swatchesX = (windowDimensions.x - (swatchesWidth + scaleSelectorWidth)) / 2;
|
|
swatchesY = windowDimensions.y - swatchHeight + 1;
|
|
|
|
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
|
|
// 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;
|
|
|
|
var voxelToolColor = WHITE_COLOR;
|
|
|
|
if (recolorToolSelected) {
|
|
recolorToolOffset = 2;
|
|
} else if (eyedropperToolSelected) {
|
|
eyedropperToolOffset = 2;
|
|
} else if (voxelToolSelected) {
|
|
if (pasteMode) {
|
|
voxelToolColor = pasteModeColor;
|
|
}
|
|
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,
|
|
color: voxelToolColor,
|
|
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
|
|
});
|
|
|
|
scaleSelector.move();
|
|
}
|
|
|
|
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)) {
|
|
newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue };
|
|
|
|
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) {
|
|
if (editToolsOn) {
|
|
var newWindowDimensions = Controller.getViewportDimensions();
|
|
if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) {
|
|
windowDimensions = newWindowDimensions;
|
|
moveTools();
|
|
}
|
|
|
|
checkControllers();
|
|
}
|
|
}
|
|
|
|
function wheelEvent(event) {
|
|
wheelPixelsMoved += event.delta;
|
|
if (Math.abs(wheelPixelsMoved) > WHEEL_PIXELS_PER_SCALE_CHANGE)
|
|
{
|
|
|
|
if (wheelPixelsMoved > 0) {
|
|
scaleSelector.decrementScale();
|
|
} else {
|
|
scaleSelector.incrementScale();
|
|
}
|
|
trackMouseEvent(event);
|
|
wheelPixelsMoved = 0;
|
|
|
|
if (isImporting) {
|
|
rescaleImport();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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.captureKeyEvents({ text: "+" });
|
|
Controller.captureKeyEvents({ text: "-" });
|
|
|
|
|
|
function scriptEnding() {
|
|
Overlays.deleteOverlay(voxelPreview);
|
|
for (var i = 0; i < linePreviewTop.length; i++) {
|
|
Overlays.deleteOverlay(linePreviewTop[i]);
|
|
Overlays.deleteOverlay(linePreviewBottom[i]);
|
|
Overlays.deleteOverlay(linePreviewLeft[i]);
|
|
Overlays.deleteOverlay(linePreviewRight[i]);
|
|
}
|
|
for (s = 0; s < numColors; s++) {
|
|
Overlays.deleteOverlay(swatches[s]);
|
|
}
|
|
Overlays.deleteOverlay(voxelTool);
|
|
Overlays.deleteOverlay(recolorTool);
|
|
Overlays.deleteOverlay(eyedropperTool);
|
|
Controller.releaseKeyEvents({ text: "+" });
|
|
Controller.releaseKeyEvents({ text: "-" });
|
|
cleanupImport();
|
|
scaleSelector.cleanup();
|
|
cleanupMenus();
|
|
}
|
|
Script.scriptEnding.connect(scriptEnding);
|
|
|
|
Script.update.connect(update);
|
|
|
|
setupVoxelMenus();
|