// // 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 // 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 zFightingSizeAdjust = 0.002; // 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; 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 = 0; // Starting color is 'Copy' mode // Create sounds for for every script actions that require one var audioOptions = new AudioInjectionOptions(); audioOptions.volume = 1.0; audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0 } ); // start with audio slightly above the avatar function SoundArray() { this.audioOptions = audioOptions this.sounds = new Array(); this.addSound = function (soundURL) { this.sounds[this.sounds.length] = new Sound(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("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Add/VA+1.raw"); addVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Add/VA+2.raw"); addVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Add/VA+3.raw"); addVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Add/VA+4.raw"); addVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Add/VA+5.raw"); addVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Add/VA+6.raw"); var delVoxelSound = new SoundArray(); delVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Del/VD+A1.raw"); delVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Del/VD+A2.raw"); delVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Del/VD+A3.raw"); delVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Del/VD+B1.raw"); delVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Del/VD+B2.raw"); delVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Del/VD+B3.raw"); var resizeVoxelSound = new SoundArray(); resizeVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Size/V+Size+Minus.raw"); resizeVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Voxel+Size/V+Size+Plus.raw"); var voxelSizeMinus = 0; var voxelSizePlus = 1; var swatchesSound = new SoundArray(); swatchesSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Swatches/Swatch+1.raw"); swatchesSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Swatches/Swatch+2.raw"); swatchesSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Swatches/Swatch+3.raw"); swatchesSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Swatches/Swatch+4.raw"); swatchesSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Swatches/Swatch+5.raw"); swatchesSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Swatches/Swatch+6.raw"); swatchesSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Swatches/Swatch+7.raw"); swatchesSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Swatches/Swatch+8.raw"); swatchesSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Swatches/Swatch+9.raw"); var undoSound = new SoundArray(); undoSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Undo/Undo+1.raw"); undoSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Undo/Undo+2.raw"); undoSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Undo/Undo+3.raw"); var scriptInitSound = new SoundArray(); scriptInitSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Script+Init/Script+Init+A.raw"); scriptInitSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Script+Init/Script+Init+B.raw"); scriptInitSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Script+Init/Script+Init+C.raw"); scriptInitSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Script+Init/Script+Init+D.raw"); var modeSwitchSound = new SoundArray(); modeSwitchSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Mode+Switch/Mode+1.raw"); modeSwitchSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Mode+Switch/Mode+2.raw"); modeSwitchSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Mode+Switch/Mode+3.raw"); var initialVoxelSound = new SoundArray(); initialVoxelSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Initial+Voxel/Initial+V.raw"); var colorInheritSound = new SoundArray(); colorInheritSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Color+Inherit/Inherit+A.raw"); colorInheritSound.addSound("https://highfidelity-public.s3.amazonaws.com/sounds/Voxel+Editing/Color+Inherit/Inherit+B.raw"); colorInheritSound.addSound("https://highfidelity-public.s3.amazonaws.com/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 = 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 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 = "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 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(), alpha: 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(), alpha: 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", { position: { 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", { position: { 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", { position: { 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, { position: { x: importPosition.x, y: 0, z: importPosition.z }, end: { x: importPosition.x + scaleSelector.scale, y: 0, z: importPosition.z } }); Overlays.editOverlay(yImportGuide, { position: { x: importPosition.x, y: importPosition.y, z: importPosition.z }, end: { x: importPosition.x, y: 0, z: importPosition.z } }); Overlays.editOverlay(zImportGuide, { position: { 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); } // 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); 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, { 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, { 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, { 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, { 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 }); colors[0] = {red: intersection.voxel.red, green: intersection.voxel.green , blue: intersection.voxel.blue }; if (copyScale) { scaleSelector.setScale(intersection.voxel.s); } moveTools(); } 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 && !isImporting && (voxelToolSelected || recolorToolSelected || eyedropperToolSelected)) { 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) { 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; } 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 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 (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") { // 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 } 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 (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 { 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) { return; } if (inspectJsIsRunning) { return; } // 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) { // 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 (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); 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); Controller.releaseKeyEvents({ text: "+" }); Controller.releaseKeyEvents({ text: "-" }); cleanupImport(); scaleSelector.cleanup(); cleanupMenus(); } Script.scriptEnding.connect(scriptEnding); Script.update.connect(update); setupMenus();