var controlHeld = false; var shiftHeld = false; Script.include([ "./libraries/toolBars.js", "./libraries/utils.js", ]); var isActive = false; var toolIconUrl = Script.resolvePath("assets/images/tools/"); var toolHeight = 50; var toolWidth = 50; var addingVoxels = false; var deletingVoxels = false; var addingSpheres = false; var deletingSpheres = false; var offAlpha = 0.8; var onAlpha = 1.0; var editSphereRadius = 4; function floorVector(v) { return { x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z) }; } var toolBar = (function() { var that = {}, toolBar, activeButton, addVoxelButton, deleteVoxelButton, addSphereButton, deleteSphereButton, addTerrainButton; function initialize() { toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.voxel.toolbar", function(windowDimensions, toolbar) { return { x: windowDimensions.x - 8 * 2 - toolbar.width * 2, y: (windowDimensions.y - toolbar.height) / 2 }; }); activeButton = toolBar.addTool({ imageURL: toolIconUrl+"voxels.svg", width: toolWidth, height: toolHeight, alpha: onAlpha, visible: true, }, false); addVoxelButton = toolBar.addTool({ imageURL: toolIconUrl + "voxel-add.svg", width: toolWidth, height: toolHeight, alpha: offAlpha, visible: false }, false); deleteVoxelButton = toolBar.addTool({ imageURL: toolIconUrl + "voxel-delete.svg", width: toolWidth, height: toolHeight, alpha: offAlpha, visible: false }, false); addSphereButton = toolBar.addTool({ imageURL: toolIconUrl + "sphere-add.svg", width: toolWidth, height: toolHeight, alpha: offAlpha, visible: false }, false); deleteSphereButton = toolBar.addTool({ imageURL: toolIconUrl + "sphere-delete.svg", width: toolWidth, height: toolHeight, alpha: offAlpha, visible: false }, false); addTerrainButton = toolBar.addTool({ imageURL: toolIconUrl + "voxel-terrain.svg", width: toolWidth, height: toolHeight, alpha: onAlpha, visible: false }, false); that.setActive(false); } function disableAllButtons() { addingVoxels = false; deletingVoxels = false; addingSpheres = false; deletingSpheres = false; toolBar.setAlpha(offAlpha, addVoxelButton); toolBar.setAlpha(offAlpha, deleteVoxelButton); toolBar.setAlpha(offAlpha, addSphereButton); toolBar.setAlpha(offAlpha, deleteSphereButton); toolBar.selectTool(addVoxelButton, false); toolBar.selectTool(deleteVoxelButton, false); toolBar.selectTool(addSphereButton, false); toolBar.selectTool(deleteSphereButton, false); } that.setActive = function(active) { if (active != isActive) { isActive = active; that.showTools(isActive); } toolBar.selectTool(activeButton, isActive); }; // Sets visibility of tool buttons, excluding the power button that.showTools = function(doShow) { toolBar.showTool(addVoxelButton, doShow); toolBar.showTool(deleteVoxelButton, doShow); toolBar.showTool(addSphereButton, doShow); toolBar.showTool(deleteSphereButton, doShow); toolBar.showTool(addTerrainButton, doShow); }; that.mousePressEvent = function(event) { var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); if (activeButton === toolBar.clicked(clickedOverlay)) { that.setActive(!isActive); return true; } if (addVoxelButton === toolBar.clicked(clickedOverlay)) { var wasAddingVoxels = addingVoxels; disableAllButtons() if (!wasAddingVoxels) { addingVoxels = true; toolBar.setAlpha(onAlpha, addVoxelButton); } return true; } if (deleteVoxelButton === toolBar.clicked(clickedOverlay)) { var wasDeletingVoxels = deletingVoxels; disableAllButtons() if (!wasDeletingVoxels) { deletingVoxels = true; toolBar.setAlpha(onAlpha, deleteVoxelButton); } return true; } if (addSphereButton === toolBar.clicked(clickedOverlay)) { var wasAddingSpheres = addingSpheres disableAllButtons() if (!wasAddingSpheres) { addingSpheres = true; toolBar.setAlpha(onAlpha, addSphereButton); } return true; } if (deleteSphereButton === toolBar.clicked(clickedOverlay)) { var wasDeletingSpheres = deletingSpheres; disableAllButtons() if (!wasDeletingSpheres) { deletingSpheres = true; toolBar.setAlpha(onAlpha, deleteSphereButton); } return true; } if (addTerrainButton === toolBar.clicked(clickedOverlay)) { addTerrainBlock(); return true; } } Window.domainChanged.connect(function() { that.setActive(false); }); that.cleanup = function() { toolBar.cleanup(); }; initialize(); return that; }()); function getTerrainAlignedLocation(pos) { var posDiv16 = Vec3.multiply(pos, 1.0 / 16.0); var posDiv16Floored = floorVector(posDiv16); return Vec3.multiply(posDiv16Floored, 16.0); } function lookupTerrainForLocation(pos) { var baseLocation = getTerrainAlignedLocation(pos); entitiesAtLoc = Entities.findEntities(baseLocation, 1.0); for (var i = 0; i < entitiesAtLoc.length; i++) { var id = entitiesAtLoc[i]; var properties = Entities.getEntityProperties(id); if (properties.name == "terrain") { return id; } } return false; } function grabLowestJointY() { var jointNames = MyAvatar.getJointNames(); var floorY = MyAvatar.position.y; for (var jointName in jointNames) { if (MyAvatar.getJointPosition(jointNames[jointName]).y < floorY) { floorY = MyAvatar.getJointPosition(jointNames[jointName]).y; } } return floorY; } function addTerrainBlock() { var baseLocation = getTerrainAlignedLocation(Vec3.sum(MyAvatar.position, { x: 8, y: 8, z: 8 })); if (baseLocation.y > MyAvatar.position.y) { baseLocation.y -= 16; } var alreadyThere = lookupTerrainForLocation(baseLocation); if (alreadyThere) { // there is already a terrain block under MyAvatar. // try in front of the avatar. facingPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(8.0, Quat.getForward(Camera.getOrientation()))); facingPosition = Vec3.sum(facingPosition, { x: 8, y: 8, z: 8 }); baseLocation = getTerrainAlignedLocation(facingPosition); alreadyThere = lookupTerrainForLocation(baseLocation); if (alreadyThere) { return null; } } var polyVoxID = addTerrainBlockNearLocation(baseLocation); if (polyVoxID) { var AvatarPositionInVoxelCoords = Entities.worldCoordsToVoxelCoords(polyVoxID, MyAvatar.position); // TODO -- how to find the avatar's feet? var topY = Math.round(AvatarPositionInVoxelCoords.y) - 4; Entities.setVoxelsInCuboid(polyVoxID, { x: 0, y: 0, z: 0 }, { x: 16, y: topY, z: 16 }, 255); } } function addTerrainBlockNearLocation(baseLocation) { var alreadyThere = lookupTerrainForLocation(baseLocation); if (alreadyThere) { return null; } var polyVoxID = Entities.addEntity({ type: "PolyVox", name: "terrain", position: baseLocation, dimensions: { x: 16, y: 16, z: 16 }, voxelVolumeSize: { x: 16, y: 64, z: 16 }, voxelSurfaceStyle: 0, xTextureURL: Script.resolvePath("assets/images/textures/dirt.jpeg"), yTextureURL: Script.resolvePath("assets/images/textures/grass.png"), zTextureURL: Script.resolvePath("assets/images/textures/dirt.jpeg") }); ////////// // stitch together the terrain with x/y/z NeighorID properties ////////// // link neighbors to this plot imXNNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 16, y: 0, z: 0 })); imYNNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 0, y: 16, z: 0 })); imZNNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 0, y: 0, z: 16 })); imXPNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: -16, y: 0, z: 0 })); imYPNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 0, y: -16, z: 0 })); imZPNeighborFor = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 0, y: 0, z: -16 })); if (imXNNeighborFor) { var properties = Entities.getEntityProperties(imXNNeighborFor); properties.xNNeighborID = polyVoxID; Entities.editEntity(imXNNeighborFor, properties); } if (imYNNeighborFor) { var properties = Entities.getEntityProperties(imYNNeighborFor); properties.yNNeighborID = polyVoxID; Entities.editEntity(imYNNeighborFor, properties); } if (imZNNeighborFor) { var properties = Entities.getEntityProperties(imZNNeighborFor); properties.zNNeighborID = polyVoxID; Entities.editEntity(imZNNeighborFor, properties); } if (imXPNeighborFor) { var properties = Entities.getEntityProperties(imXPNeighborFor); properties.xPNeighborID = polyVoxID; Entities.editEntity(imXPNeighborFor, properties); } if (imYPNeighborFor) { var properties = Entities.getEntityProperties(imYPNeighborFor); properties.yPNeighborID = polyVoxID; Entities.editEntity(imYPNeighborFor, properties); } if (imZPNeighborFor) { var properties = Entities.getEntityProperties(imZPNeighborFor); properties.zPNeighborID = polyVoxID; Entities.editEntity(imZPNeighborFor, properties); } // link this plot to its neighbors var properties = Entities.getEntityProperties(polyVoxID); properties.xNNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: -16, y: 0, z: 0 })); properties.yNNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 0, y: -16, z: 0 })); properties.zNNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 0, y: 0, z: -16 })); properties.xPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 16, y: 0, z: 0 })); properties.yPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 0, y: 16, z: 0 })); properties.zPNeighborID = lookupTerrainForLocation(Vec3.sum(baseLocation, { x: 0, y: 0, z: 16 })); Entities.editEntity(polyVoxID, properties); return polyVoxID; } function attemptVoxelChangeForEntity(entityID, pickRayDir, intersectionLocation) { var properties = Entities.getEntityProperties(entityID); if (properties.type != "PolyVox") { return false; } if (addingVoxels == false && deletingVoxels == false && addingSpheres == false && deletingSpheres == false) { return false; } var voxelOrigin = Entities.worldCoordsToVoxelCoords(entityID, Vec3.subtract(intersectionLocation, pickRayDir)); var voxelPosition = Entities.worldCoordsToVoxelCoords(entityID, intersectionLocation); var pickRayDirInVoxelSpace = Vec3.subtract(voxelPosition, voxelOrigin); pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace); var doAdd = addingVoxels; var doDelete = deletingVoxels; var doAddSphere = addingSpheres; var doDeleteSphere = deletingSpheres; if (controlHeld) { if (doAdd) { doAdd = false; doDelete = true; } else if (doDelete) { doDelete = false; doAdd = true; } else if (doAddSphere) { doAddSphere = false; doDeleteSphere = true; } else if (doDeleteSphere) { doDeleteSphere = false; doAddSphere = true; } } if (doDelete) { var toErasePosition = Vec3.sum(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); return Entities.setVoxel(entityID, floorVector(toErasePosition), 0); } if (doAdd) { var toDrawPosition = Vec3.subtract(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1)); return Entities.setVoxel(entityID, floorVector(toDrawPosition), 255); } if (doDeleteSphere) { var toErasePosition = intersectionLocation; return Entities.setVoxelSphere(entityID, floorVector(toErasePosition), editSphereRadius, 0); } if (doAddSphere) { var toDrawPosition = intersectionLocation; return Entities.setVoxelSphere(entityID, floorVector(toDrawPosition), editSphereRadius, 255); } } function attemptVoxelChange(pickRayDir, intersection) { var ids; ids = Entities.findEntities(intersection.intersection, editSphereRadius + 1.0); if (ids.indexOf(intersection.entityID) < 0) { ids.push(intersection.entityID); } var success = false; for (var i = 0; i < ids.length; i++) { var entityID = ids[i]; success |= attemptVoxelChangeForEntity(entityID, pickRayDir, intersection.intersection) } return success; } function mousePressEvent(event) { if (!event.isLeftButton) { return; } if (toolBar.mousePressEvent(event)) { return; } var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking if (intersection.intersects) { if (attemptVoxelChange(pickRay.direction, intersection)) { return; } } // if the PolyVox entity is empty, we can't pick against its "on" voxels. try picking against its // bounding box, instead. intersection = Entities.findRayIntersection(pickRay, false); // bounding box picking if (intersection.intersects) { attemptVoxelChange(pickRay.direction, intersection); } } function keyPressEvent(event) { if (event.text == "CONTROL") { controlHeld = true; } if (event.text == "SHIFT") { shiftHeld = true; } } function keyReleaseEvent(event) { if (event.text == "CONTROL") { controlHeld = false; } if (event.text == "SHIFT") { shiftHeld = false; } } function cleanup() { toolBar.cleanup(); } Controller.mousePressEvent.connect(mousePressEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); Script.scriptEnding.connect(cleanup);