From 829918e6c0fea90439cd2829a5f9f0db7583b04e Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 11 Jul 2014 17:23:13 -0700 Subject: [PATCH 1/7] Added grenadeLauncher.js to examples. Sorry in advance for any damage --- examples/grenadeLauncher.js | 358 ++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 examples/grenadeLauncher.js diff --git a/examples/grenadeLauncher.js b/examples/grenadeLauncher.js new file mode 100644 index 0000000000..f20e915f07 --- /dev/null +++ b/examples/grenadeLauncher.js @@ -0,0 +1,358 @@ +// +// grenadeLauncher.js +// examples +// Created by Ben Arnold on 7/11/14. +// This is a modified version of gun.js by Brad Hefta-Gaub. +// +// Copyright 2013 High Fidelity, Inc. +// +// This is an example script that turns the hydra controllers and mouse into a particle gun. +// It reads the controller, watches for trigger pulls, and launches particles. +// When particles collide with voxels they blow little holes out of the voxels. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +function getRandomFloat(min, max) { + return Math.random() * (max - min) + min; +} + +var lastX = 0; +var lastY = 0; +var yawFromMouse = 0; +var pitchFromMouse = 0; +var isMouseDown = false; + +var BULLET_VELOCITY = 3.0; +var MIN_THROWER_DELAY = 1000; +var MAX_THROWER_DELAY = 1000; +var LEFT_BUTTON_1 = 1; +var LEFT_BUTTON_3 = 3; +var RELOAD_INTERVAL = 5; + +var showScore = false; + +// Load some sound to use for loading and firing +var fireSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/GUN-SHOT2.raw"); +var loadSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/Gun_Reload_Weapon22.raw"); +var impactSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/BulletImpact2.raw"); +var targetHitSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/hit.raw"); +var targetLaunchSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/shoot.raw"); + +var gunModel = "http://public.highfidelity.io/models/attachments/HaloGun.fst"; + +var audioOptions = new AudioInjectionOptions(); +audioOptions.volume = 0.9; + +var shotsFired = 0; + +var shotTime = new Date(); + +// initialize our triggers +var triggerPulled = new Array(); +var numberOfTriggers = Controller.getNumberOfTriggers(); +for (t = 0; t < numberOfTriggers; t++) { + triggerPulled[t] = false; +} + +var isLaunchButtonPressed = false; + +var score = 0; + +// Create a reticle image in center of screen +var screenSize = Controller.getViewportDimensions(); +var reticle = Overlays.addOverlay("image", { + x: screenSize.x / 2 - 16, + y: screenSize.y / 2 - 16, + width: 32, + height: 32, + imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/reticle.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + +if (showScore) { + var text = Overlays.addOverlay("text", { + x: screenSize.x / 2 - 100, + y: screenSize.y / 2 - 50, + width: 150, + height: 50, + color: { red: 0, green: 0, blue: 0}, + textColor: { red: 255, green: 0, blue: 0}, + topMargin: 4, + leftMargin: 4, + text: "Score: " + score + }); +} + +function printVector(string, vector) { + print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); +} + +function shootBullet(position, velocity) { + var BULLET_SIZE = 0.1; + var BULLET_GRAVITY = -3.0; + //Creates a grenade with a reasonable lifetime so that one is less likely to accidentally blow up + //far away voxels + Particles.addParticle( + { position: position, + radius: BULLET_SIZE, + color: { red: 10, green: 10, blue: 10 }, + velocity: velocity, + gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, + lifetime: 10.0, + damping: 0 }); + + // Play firing sounds + audioOptions.position = position; + Audio.playSound(fireSound, audioOptions); + shotsFired++; + if ((shotsFired % RELOAD_INTERVAL) == 0) { + Audio.playSound(loadSound, audioOptions); + } +} + +function shootTarget() { + var TARGET_SIZE = 0.25; + var TARGET_GRAVITY = -0.6; + var TARGET_UP_VELOCITY = 3.0; + var TARGET_FWD_VELOCITY = 5.0; + var DISTANCE_TO_LAUNCH_FROM = 3.0; + var camera = Camera.getPosition(); + //printVector("camera", camera); + var targetDirection = Quat.angleAxis(getRandomFloat(-20.0, 20.0), { x:0, y:1, z:0 }); + targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); + var forwardVector = Quat.getFront(targetDirection); + //printVector("forwardVector", forwardVector); + var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM)); + //printVector("newPosition", newPosition); + var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY); + velocity.y += TARGET_UP_VELOCITY; + //printVector("velocity", velocity); + + Particles.addParticle( + { position: newPosition, + radius: TARGET_SIZE, + color: { red: 0, green: 200, blue: 200 }, + velocity: velocity, + gravity: { x: 0, y: TARGET_GRAVITY, z: 0 }, + lifetime: 1000.0, + damping: 0.99 }); + + // Record start time + shotTime = new Date(); + + // Play target shoot sound + audioOptions.position = newPosition; + Audio.playSound(targetLaunchSound, audioOptions); +} + + + +function particleCollisionWithVoxel(particle, voxel, collision) { + var VOXEL_SIZE = 0.5; + // Don't make this big. I mean it. + var CRATER_RADIUS = 5; + var particleProperties = Particles.getParticleProperties(particle); + var position = particleProperties.position; + Particles.deleteParticle(particle); + + audioOptions.position = collision.contactPoint; + Audio.playSound(impactSound, audioOptions); + + // Make a crater + var center = collision.contactPoint; + var RADIUS = CRATER_RADIUS * VOXEL_SIZE; + var RADIUS2 = RADIUS * RADIUS; + var distance2; + var dx; + var dy; + var dz; + for (var x = center.x - RADIUS; x <= center.x + RADIUS; x += VOXEL_SIZE) { + for (var y = center.y - RADIUS; y <= center.y + RADIUS; y += VOXEL_SIZE) { + for (var z = center.z - RADIUS; z <= center.z + RADIUS; z += VOXEL_SIZE) { + dx = Math.abs(x - center.x); + dy = Math.abs(y - center.y); + dz = Math.abs(z - center.z); + distance2 = dx * dx + dy * dy + dz * dz; + if (distance2 <= RADIUS2) { + Voxels.eraseVoxel(x, y, z, VOXEL_SIZE); + } + } + } + } +} + +function particleCollisionWithParticle(particle1, particle2, collision) { + score++; + if (showScore) { + Overlays.editOverlay(text, { text: "Score: " + score } ); + } + + // Record shot time + var endTime = new Date(); + var msecs = endTime.valueOf() - shotTime.valueOf(); + //print("hit, msecs = " + msecs); + //Vec3.print("penetration = ", collision.penetration); + //Vec3.print("contactPoint = ", collision.contactPoint); + Particles.deleteParticle(particle1); + Particles.deleteParticle(particle2); + // play the sound near the camera so the shooter can hear it + audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + Audio.playSound(targetHitSound, audioOptions); +} + +function keyPressEvent(event) { + // if our tools are off, then don't do anything + if (event.text == "t") { + var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; + Script.setTimeout(shootTarget, time); + } else if (event.text == ".") { + shootFromMouse(); + } else if (event.text == "r") { + playLoadSound(); + } +} + +function playLoadSound() { + audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + Audio.playSound(loadSound, audioOptions); +} + +//MyAvatar.attach(gunModel, "RightHand", {x: -0.02, y: -.14, z: 0.07}, Quat.fromPitchYawRollDegrees(-70, -151, 72), 0.20); +MyAvatar.attach(gunModel, "LeftHand", {x: -0.02, y: -.14, z: 0.07}, Quat.fromPitchYawRollDegrees(-70, -151, 72), 0.20); + +// Give a bit of time to load before playing sound +Script.setTimeout(playLoadSound, 2000); + +function update(deltaTime) { + + // Check for mouseLook movement, update rotation + // rotate body yaw for yaw received from controller or mouse + var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } )); + MyAvatar.orientation = newOrientation; + yawFromMouse = 0; + + // apply pitch from controller or mouse + var newPitch = MyAvatar.headPitch + pitchFromMouse; + MyAvatar.headPitch = newPitch; + pitchFromMouse = 0; + + // Check hydra controller for launch button press + if (!isLaunchButtonPressed && Controller.isButtonPressed(LEFT_BUTTON_3)) { + isLaunchButtonPressed = true; + var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; + Script.setTimeout(shootTarget, time); + } else if (isLaunchButtonPressed && !Controller.isButtonPressed(LEFT_BUTTON_3)) { + isLaunchButtonPressed = false; + + } + + // Check hydra controller for trigger press + + var numberOfTriggers = Controller.getNumberOfTriggers(); + var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); + var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; + + // this is expected for hydras + if (numberOfTriggers == 2 && controllersPerTrigger == 2) { + for (var t = 0; t < numberOfTriggers; t++) { + var shootABullet = false; + var triggerValue = Controller.getTriggerValue(t); + + if (triggerPulled[t]) { + // must release to at least 0.1 + if (triggerValue < 0.1) { + triggerPulled[t] = false; // unpulled + } + } else { + // must pull to at least 0.9 + if (triggerValue > 0.9) { + triggerPulled[t] = true; // pulled + shootABullet = true; + } + } + + if (shootABullet) { + + var palmController = t * controllersPerTrigger; + var palmPosition = Controller.getSpatialControlPosition(palmController); + + var fingerTipController = palmController + 1; + var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController); + + var palmToFingerTipVector = + { x: (fingerTipPosition.x - palmPosition.x), + y: (fingerTipPosition.y - palmPosition.y), + z: (fingerTipPosition.z - palmPosition.z) }; + + // just off the front of the finger tip + var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2, + y: fingerTipPosition.y + palmToFingerTipVector.y/2, + z: fingerTipPosition.z + palmToFingerTipVector.z/2}; + + var linearVelocity = 25; + + var velocity = { x: palmToFingerTipVector.x * linearVelocity, + y: palmToFingerTipVector.y * linearVelocity, + z: palmToFingerTipVector.z * linearVelocity }; + + shootBullet(position, velocity); + } + } + } +} + +function mousePressEvent(event) { + isMouseDown = true; + lastX = event.x; + lastY = event.y; + //audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + //Audio.playSound(loadSound, audioOptions); +} + +function shootFromMouse() { + var DISTANCE_FROM_CAMERA = 2.0; + var camera = Camera.getPosition(); + var forwardVector = Quat.getFront(Camera.getOrientation()); + var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); + var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); + shootBullet(newPosition, velocity); +} + +function mouseReleaseEvent(event) { + // position + isMouseDown = false; +} + +function mouseMoveEvent(event) { + //Move the camera if LEFT_BUTTON_1 is pressed + if (Controller.isButtonPressed(LEFT_BUTTON_1)) { + var MOUSE_YAW_SCALE = -0.25; + var MOUSE_PITCH_SCALE = -12.5; + var FIXED_MOUSE_TIMESTEP = 0.016; + yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP); + pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); + lastX = event.x; + lastY = event.y; + } +} + +function scriptEnding() { + Overlays.deleteOverlay(reticle); + Overlays.deleteOverlay(text); + MyAvatar.detachOne(gunModel); +} + +Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel); +Particles.particleCollisionWithParticle.connect(particleCollisionWithParticle); +Script.scriptEnding.connect(scriptEnding); +Script.update.connect(update); +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.keyPressEvent.connect(keyPressEvent); + + + From 13960cd6456bd3a488c81be4a6e789bc30723f58 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 14 Jul 2014 16:16:07 -0700 Subject: [PATCH 2/7] Basic falling sand script --- examples/fallingSand.js | 91 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 examples/fallingSand.js diff --git a/examples/fallingSand.js b/examples/fallingSand.js new file mode 100644 index 0000000000..015f9e23ff --- /dev/null +++ b/examples/fallingSand.js @@ -0,0 +1,91 @@ +var SAND_LIFETIME = 100.0; +var UPDATE_RATE = 1.0; +var SAND_SIZE = 0.125; + +var sandArray = []; +var numSand = 0; + + +var red = 255; +var green = 0; +var blue = 0; + +var SOURCE_POSITION = MyAvatar.position; + +function update() { + + for (var i = 0; i < numSand; i++) { + updateSand(i); + } + + var voxel = Voxels.getVoxelAt(SOURCE_POSITION.x, SOURCE_POSITION.y, SOURCE_POSITION.z, SAND_SIZE); + if (voxel.s == 0) { + sandArray[numSand] = {x: SOURCE_POSITION.x, y: SOURCE_POSITION.y, z: SOURCE_POSITION.z, s: SAND_SIZE, r: red, g: green, b: blue}; + numSand++; + Voxels.setVoxel(SOURCE_POSITION.x, SOURCE_POSITION.y, SOURCE_POSITION.z, SAND_SIZE, red, green, blue); + } +} + +function isVoxelEmpty(x, y, z) { + var adjacent = Voxels.getVoxelAt(x, y, z, SAND_SIZE); + return (adjacent.s == 0); +} + +function tryMoveVoxel(voxel, x, y, z) { + //If the adjacent voxel is empty, we will move to it. + if (isVoxelEmpty(x, y, z)) { + moveVoxel(voxel, x, y, z); + return true; + } + return false; +} + +function moveVoxel(voxel, x, y, z) { + Voxels.eraseVoxel(voxel.x, voxel.y, voxel.z, SAND_SIZE); + Voxels.setVoxel(x, y, z, SAND_SIZE, red, green, blue); + voxel.x = x; + voxel.y = y; + voxel.z = z; +} + +function updateSand(i) { + var voxel = sandArray[i]; + + //Down + if (tryMoveVoxel(voxel, voxel.x, voxel.y - SAND_SIZE, voxel.z)) return true; + //Left + if (isVoxelEmpty(voxel.x - SAND_SIZE, voxel.y, voxel.z) && + isVoxelEmpty(voxel.x - SAND_SIZE, voxel.y - SAND_SIZE, voxel.z)) { + moveVoxel(voxel, voxel.x - SAND_SIZE, voxel.y, voxel.z); + return true; + } + //Back + if (isVoxelEmpty(voxel.x, voxel.y, voxel.z - SAND_SIZE) && + isVoxelEmpty(voxel.x, voxel.y - SAND_SIZE, voxel.z - SAND_SIZE)) { + moveVoxel(voxel, voxel.x, voxel.y, voxel.z - SAND_SIZE); + return true; + } + //Right + if (isVoxelEmpty(voxel.x + SAND_SIZE, voxel.y, voxel.z) && + isVoxelEmpty(voxel.x + SAND_SIZE, voxel.y - SAND_SIZE, voxel.z)) { + moveVoxel(voxel, voxel.x + SAND_SIZE, voxel.y, voxel.z); + return true; + } + //Front + if (isVoxelEmpty(voxel.x, voxel.y, voxel.z + SAND_SIZE) && + isVoxelEmpty(voxel.x, voxel.y - SAND_SIZE, voxel.z + SAND_SIZE)) { + moveVoxel(voxel, voxel.x, voxel.y, voxel.z + SAND_SIZE); + return true; + } + return false; +} + +function scriptEnding() { + for (var i = 0; i < numSand; i++) { + var voxel = sandArray[i]; + Voxels.eraseVoxel(voxel.x, voxel.y, voxel.z, SAND_SIZE); + } +} + +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); From 4991bf37a2f2ae23e4bf0ca398c6dae30b731884 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 14 Jul 2014 17:54:34 -0700 Subject: [PATCH 3/7] Added a brush for the voxel sand script --- examples/fallingSand.js | 524 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 491 insertions(+), 33 deletions(-) diff --git a/examples/fallingSand.js b/examples/fallingSand.js index 015f9e23ff..a1ba7f092a 100644 --- a/examples/fallingSand.js +++ b/examples/fallingSand.js @@ -1,39 +1,485 @@ +// +// fallingSand.js +// examples +// +// Created by Ben Arnold on 7/14/14. +// Copyright 2014 High Fidelity, Inc. +// +// This sample script allows the user to grow different types of plants on the voxels +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z fighting +var previewLineWidth = 2.0; + +var voxelSize = 1; +var windowDimensions = Controller.getViewportDimensions(); +var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; + +var MAX_VOXEL_SCALE_POWER = 5; +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 linePreviewTop = 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 + }); + +var linePreviewBottom = 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 + }); + +var linePreviewLeft = 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 + }); + +var linePreviewRight = 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 + }); + + +var UIColor = { red: 119, green: 103, blue: 53}; +var activeUIColor = { red: 234, green: 206, blue: 106}; + +var toolHeight = 50; +var toolWidth = 50; + +var editToolsOn = true; + +var voxelToolSelected = false; + +var scaleSelectorWidth = 144; +var scaleSelectorHeight = 37; + +var scaleSelectorX = windowDimensions.x / 5.0; +var scaleSelectorY = windowDimensions.y - scaleSelectorHeight; + +var voxelTool = Overlays.addOverlay("image", { + x: scaleSelectorX + scaleSelectorWidth + 1, y: windowDimensions.y - toolHeight, width: toolWidth, height: toolHeight, + subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: toolIconUrl + "voxel-tool.svg", + visible: editToolsOn, + color: UIColor, + alpha: 0.9 + }); + +var copyScale = true; +function ScaleSelector() { + this.x = scaleSelectorX; + this.y = scaleSelectorY; + 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, + color: activeUIColor + }); + 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, + color: activeUIColor + }); + 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, + color: activeUIColor + }); + 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)); + 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(); + } + } + + this.decrementScale = function() { + copyScale = false; + if (MIN_VOXEL_SCALE_POWER < this.power) { + --this.power; + this.scale /= 2.0; + this.update(); + } + } + + 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(); + +function calculateVoxelFromIntersection(intersection, operation) { + + var resultVoxel; + + 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) { + 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)); + + 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 }; + var 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)); + + // 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; +} + +var trackLastMouseX = 0; +var trackLastMouseY = 0; + +function showPreviewLines() { + + var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); + + var intersection = Voxels.findRayIntersection(pickRay); + + if (intersection.intersects) { + var resultVoxel = calculateVoxelFromIntersection(intersection, ""); + Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true }); + Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true }); + Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true }); + Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true }); + } else { + Overlays.editOverlay(linePreviewTop, { visible: false }); + Overlays.editOverlay(linePreviewBottom, { visible: false }); + Overlays.editOverlay(linePreviewLeft, { visible: false }); + Overlays.editOverlay(linePreviewRight, { visible: false }); + } +} + +function mouseMoveEvent(event) { + trackLastMouseX = event.x; + trackLastMouseY = event.y; + if (!voxelToolSelected) { + return; + } + showPreviewLines(); +} + +var BRUSH_RADIUS = 5; + +function mousePressEvent(event) { + var mouseX = event.x; + var mouseY = event.y; + + var clickedOnSomething = false; + // Check if we clicked an overlay + var clickedOverlay = Overlays.getOverlayAtPoint({x: mouseX, y: mouseY}); + + if (clickedOverlay == voxelTool) { + voxelToolSelected = !voxelToolSelected; + + if (voxelToolSelected == true) { + Overlays.editOverlay(voxelTool, { + color: activeUIColor + }); + } else { + Overlays.editOverlay(voxelTool, { + color: UIColor + }); + } + + clickedOnSomething = true; + } else if (scaleSelector.clicked(event.x, event.y)) { + clickedOnSomething = true; + voxelSize = scaleSelector.scale; + } + + // Return if we clicked on the UI or the voxel tool is disabled + if (clickedOnSomething || !voxelToolSelected) { + return; + } + + // Compute the picking ray for the click + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = Voxels.findRayIntersection(pickRay); + var resultVoxel = calculateVoxelFromIntersection(intersection, "add"); + + + //Add a clump of sand voxels + makeSphere(resultVoxel.x, resultVoxel.y, resultVoxel.z, voxelSize * BRUSH_RADIUS, voxelSize); +} + var SAND_LIFETIME = 100.0; var UPDATE_RATE = 1.0; -var SAND_SIZE = 0.125; var sandArray = []; var numSand = 0; - -var red = 255; -var green = 0; -var blue = 0; - -var SOURCE_POSITION = MyAvatar.position; +var red = 234; +var green = 206; +var blue = 106; function update() { - + //Update all sand in our sandArray for (var i = 0; i < numSand; i++) { updateSand(i); } +} + +function makeSphere(cx, cy, cz, r, voxelSize) { + + var r2 = r * r; + var distance2; + var dx; + var dy; + var dz; - var voxel = Voxels.getVoxelAt(SOURCE_POSITION.x, SOURCE_POSITION.y, SOURCE_POSITION.z, SAND_SIZE); - if (voxel.s == 0) { - sandArray[numSand] = {x: SOURCE_POSITION.x, y: SOURCE_POSITION.y, z: SOURCE_POSITION.z, s: SAND_SIZE, r: red, g: green, b: blue}; - numSand++; - Voxels.setVoxel(SOURCE_POSITION.x, SOURCE_POSITION.y, SOURCE_POSITION.z, SAND_SIZE, red, green, blue); + Voxels.setVoxel(cx, cy, cz, voxelSize, red, green, blue); + sandArray[numSand] = { x: cx, y: cy, z: cz, s: voxelSize, r: red, g: green, b: blue }; + numSand++; + return 0; + + for (var x = cx - r; x <= cx + r; x += voxelSize) { + for (var y = cy - r; y <= cy + r; y += voxelSize) { + for (var z = cz - r; z <= cz + r; z += voxelSize) { + dx = Math.abs(x - cx); + dy = Math.abs(y - cy); + dz = Math.abs(z - cz); + distance2 = dx * dx + dy * dy + dz * dz; + if (distance2 <= r2 && isVoxelEmpty(x, y, z, voxelSize)) { + Voxels.setVoxel(x, y, z, voxelSize, red, green, blue); + sandArray[numSand] = { x: x, y: y, z: z, s: voxelSize, r: red, g: green, b: blue }; + numSand++; + } + } + } } } -function isVoxelEmpty(x, y, z) { - var adjacent = Voxels.getVoxelAt(x, y, z, SAND_SIZE); +function isVoxelEmpty(x, y, z, s) { + var adjacent = Voxels.getVoxelAt(x, y, z, s); + print(adjacent.s); return (adjacent.s == 0); } function tryMoveVoxel(voxel, x, y, z) { //If the adjacent voxel is empty, we will move to it. - if (isVoxelEmpty(x, y, z)) { + if (isVoxelEmpty(x, y, z, voxel.s)) { moveVoxel(voxel, x, y, z); return true; } @@ -41,8 +487,8 @@ function tryMoveVoxel(voxel, x, y, z) { } function moveVoxel(voxel, x, y, z) { - Voxels.eraseVoxel(voxel.x, voxel.y, voxel.z, SAND_SIZE); - Voxels.setVoxel(x, y, z, SAND_SIZE, red, green, blue); + Voxels.eraseVoxel(voxel.x, voxel.y, voxel.z, voxel.s); + Voxels.setVoxel(x, y, z, voxel.s, red, green, blue); voxel.x = x; voxel.y = y; voxel.z = z; @@ -50,31 +496,32 @@ function moveVoxel(voxel, x, y, z) { function updateSand(i) { var voxel = sandArray[i]; - + var size = voxel.s; + print("start"); //Down - if (tryMoveVoxel(voxel, voxel.x, voxel.y - SAND_SIZE, voxel.z)) return true; + if (tryMoveVoxel(voxel, voxel.x, voxel.y - size, voxel.z)) { + return true; + } //Left - if (isVoxelEmpty(voxel.x - SAND_SIZE, voxel.y, voxel.z) && - isVoxelEmpty(voxel.x - SAND_SIZE, voxel.y - SAND_SIZE, voxel.z)) { - moveVoxel(voxel, voxel.x - SAND_SIZE, voxel.y, voxel.z); + if (isVoxelEmpty(voxel.x - size, voxel.y, voxel.z) && isVoxelEmpty(voxel.x - size, voxel.y - size, voxel.z)) { + moveVoxel(voxel, voxel.x - size, voxel.y, voxel.z); + print("LEFT"); return true; } //Back - if (isVoxelEmpty(voxel.x, voxel.y, voxel.z - SAND_SIZE) && - isVoxelEmpty(voxel.x, voxel.y - SAND_SIZE, voxel.z - SAND_SIZE)) { - moveVoxel(voxel, voxel.x, voxel.y, voxel.z - SAND_SIZE); + if (isVoxelEmpty(voxel.x, voxel.y, voxel.z - size) && isVoxelEmpty(voxel.x, voxel.y - size, voxel.z - size)) { + moveVoxel(voxel, voxel.x, voxel.y, voxel.z - size); return true; } //Right - if (isVoxelEmpty(voxel.x + SAND_SIZE, voxel.y, voxel.z) && - isVoxelEmpty(voxel.x + SAND_SIZE, voxel.y - SAND_SIZE, voxel.z)) { - moveVoxel(voxel, voxel.x + SAND_SIZE, voxel.y, voxel.z); + if (isVoxelEmpty(voxel.x + size, voxel.y, voxel.z) && isVoxelEmpty(voxel.x + size, voxel.y - size, voxel.z)) { + moveVoxel(voxel, voxel.x + size, voxel.y, voxel.z); + print("Right"); return true; } //Front - if (isVoxelEmpty(voxel.x, voxel.y, voxel.z + SAND_SIZE) && - isVoxelEmpty(voxel.x, voxel.y - SAND_SIZE, voxel.z + SAND_SIZE)) { - moveVoxel(voxel, voxel.x, voxel.y, voxel.z + SAND_SIZE); + if (isVoxelEmpty(voxel.x, voxel.y, voxel.z + size) && isVoxelEmpty(voxel.x, voxel.y - size, voxel.z + size)) { + moveVoxel(voxel, voxel.x, voxel.y, voxel.z + size); return true; } return false; @@ -83,9 +530,20 @@ function updateSand(i) { function scriptEnding() { for (var i = 0; i < numSand; i++) { var voxel = sandArray[i]; - Voxels.eraseVoxel(voxel.x, voxel.y, voxel.z, SAND_SIZE); + Voxels.eraseVoxel(voxel.x, voxel.y, voxel.z, voxel.s); } + Overlays.deleteOverlay(linePreviewTop); + Overlays.deleteOverlay(linePreviewBottom); + Overlays.deleteOverlay(linePreviewLeft); + Overlays.deleteOverlay(linePreviewRight); + scaleSelector.cleanup(); + Overlays.deleteOverlay(voxelTool); } +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); + Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); + +Voxels.setPacketsPerSecond(10000); \ No newline at end of file From b339e9c2af09cf63a0887354614f4ac15ad94871 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 15 Jul 2014 11:44:59 -0700 Subject: [PATCH 4/7] Added getVoxelEnclosingPointBlocking and improved sand script --- examples/fallingSand.js | 50 ++++++++++++++----- .../voxels/src/VoxelsScriptingInterface.cpp | 17 +++++++ .../voxels/src/VoxelsScriptingInterface.h | 5 ++ 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/examples/fallingSand.js b/examples/fallingSand.js index a1ba7f092a..fcca0c6d5f 100644 --- a/examples/fallingSand.js +++ b/examples/fallingSand.js @@ -430,6 +430,12 @@ var UPDATE_RATE = 1.0; var sandArray = []; var numSand = 0; +//These arrays are used to buffer add/remove operations so they can be batched together +var addArray = []; +var addArraySize = 0; +var removeArray = []; +var removeArraySize = 0; + var red = 234; var green = 206; var blue = 106; @@ -439,8 +445,23 @@ function update() { for (var i = 0; i < numSand; i++) { updateSand(i); } + + //Remove all voxels that need to be moved + for (var i = 0; i < removeArraySize; i++) { + var elem = removeArray[i]; + Voxels.eraseVoxel(elem.x, elem.y, elem.z, elem.s); + } + removeArraySize = 0; + + //Add all voxels that have moved + for (var i = 0; i < addArraySize; i++) { + var elem = addArray[i]; + Voxels.setVoxel(elem.x, elem.y, elem.z, elem.s, elem.r, elem.g, elem.b); + } + addArraySize = 0; } +//Adds a sphere of sand at the center cx,cy,cz function makeSphere(cx, cy, cz, r, voxelSize) { var r2 = r * r; @@ -449,7 +470,7 @@ function makeSphere(cx, cy, cz, r, voxelSize) { var dy; var dz; - Voxels.setVoxel(cx, cy, cz, voxelSize, red, green, blue); + Voxels.setVoxel(cx, cy, cz, voxelSize, red, green, blue); sandArray[numSand] = { x: cx, y: cy, z: cz, s: voxelSize, r: red, g: green, b: blue }; numSand++; return 0; @@ -472,9 +493,13 @@ function makeSphere(cx, cy, cz, r, voxelSize) { } function isVoxelEmpty(x, y, z, s) { - var adjacent = Voxels.getVoxelAt(x, y, z, s); - print(adjacent.s); - return (adjacent.s == 0); + var halfSize = s / 2; + var point = {x: x + halfSize, y: y + halfSize, z: z + halfSize }; + + var adjacent = Voxels.getVoxelEnclosingPointBlocking(point); + + //If color is all 0, we assume its air. + return (adjacent.red == 0 && adjacent.green == 0 && adjacent.blue == 0); } function tryMoveVoxel(voxel, x, y, z) { @@ -487,8 +512,8 @@ function tryMoveVoxel(voxel, x, y, z) { } function moveVoxel(voxel, x, y, z) { - Voxels.eraseVoxel(voxel.x, voxel.y, voxel.z, voxel.s); - Voxels.setVoxel(x, y, z, voxel.s, red, green, blue); + removeArray[removeArraySize++] = {x: voxel.x, y: voxel.y, z: voxel.z, s: voxel.s}; + addArray[addArraySize++] = {x: x, y: y, z: z, s: voxel.s, r: red, g: green, b: blue}; voxel.x = x; voxel.y = y; voxel.z = z; @@ -497,30 +522,29 @@ function moveVoxel(voxel, x, y, z) { function updateSand(i) { var voxel = sandArray[i]; var size = voxel.s; - print("start"); + //Down if (tryMoveVoxel(voxel, voxel.x, voxel.y - size, voxel.z)) { return true; } + return; //Left - if (isVoxelEmpty(voxel.x - size, voxel.y, voxel.z) && isVoxelEmpty(voxel.x - size, voxel.y - size, voxel.z)) { + if (isVoxelEmpty(voxel.x - size, voxel.y, voxel.z, size) && isVoxelEmpty(voxel.x - size, voxel.y - size, voxel.z, size)) { moveVoxel(voxel, voxel.x - size, voxel.y, voxel.z); - print("LEFT"); return true; } //Back - if (isVoxelEmpty(voxel.x, voxel.y, voxel.z - size) && isVoxelEmpty(voxel.x, voxel.y - size, voxel.z - size)) { + if (isVoxelEmpty(voxel.x, voxel.y, voxel.z - size, size) && isVoxelEmpty(voxel.x, voxel.y - size, voxel.z - size, size)) { moveVoxel(voxel, voxel.x, voxel.y, voxel.z - size); return true; } //Right - if (isVoxelEmpty(voxel.x + size, voxel.y, voxel.z) && isVoxelEmpty(voxel.x + size, voxel.y - size, voxel.z)) { + if (isVoxelEmpty(voxel.x + size, voxel.y, voxel.z, size) && isVoxelEmpty(voxel.x + size, voxel.y - size, voxel.z, size)) { moveVoxel(voxel, voxel.x + size, voxel.y, voxel.z); - print("Right"); return true; } //Front - if (isVoxelEmpty(voxel.x, voxel.y, voxel.z + size) && isVoxelEmpty(voxel.x, voxel.y - size, voxel.z + size)) { + if (isVoxelEmpty(voxel.x, voxel.y, voxel.z + size, size) && isVoxelEmpty(voxel.x, voxel.y - size, voxel.z + size, size)) { moveVoxel(voxel, voxel.x, voxel.y, voxel.z + size); return true; } diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index 2aad0f7a76..e877f99760 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -187,4 +187,21 @@ VoxelDetail VoxelsScriptingInterface::getVoxelEnclosingPoint(const glm::vec3& po return result; } +VoxelDetail VoxelsScriptingInterface::getVoxelEnclosingPointBlocking(const glm::vec3& point) { + VoxelDetail result = { 0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 0 }; + if (_tree) { + OctreeElement* element = _tree->getElementEnclosingPoint(point / (float)TREE_SCALE, Octree::Lock); + if (element) { + VoxelTreeElement* voxel = static_cast(element); + result.x = voxel->getCorner().x; + result.y = voxel->getCorner().y; + result.z = voxel->getCorner().z; + result.s = voxel->getScale(); + result.red = voxel->getColor()[0]; + result.green = voxel->getColor()[1]; + result.blue = voxel->getColor()[2]; + } + } + return result; +} diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h index e5f2abf629..787c37fb20 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -90,6 +90,11 @@ public: /// \return VoxelDetail - if no voxel encloses the point then VoxelDetail items will be 0 Q_INVOKABLE VoxelDetail getVoxelEnclosingPoint(const glm::vec3& point); + /// checks the local voxel tree for the smallest voxel enclosing the point and uses a blocking lock + /// \param point the x,y,z coordinates of the point (in meter units) + /// \return VoxelDetail - if no voxel encloses the point then VoxelDetail items will be 0 + Q_INVOKABLE VoxelDetail getVoxelEnclosingPointBlocking(const glm::vec3& point); + private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode RayToVoxelIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType); From f6a184fc2bbdb03be0d4791eebdd59d056bd895a Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 16 Jul 2014 11:10:59 -0700 Subject: [PATCH 5/7] Bug fixes in fallingSand --- examples/fallingSand.js | 155 +++++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 49 deletions(-) diff --git a/examples/fallingSand.js b/examples/fallingSand.js index fcca0c6d5f..95f1bf3764 100644 --- a/examples/fallingSand.js +++ b/examples/fallingSand.js @@ -380,7 +380,7 @@ function mouseMoveEvent(event) { showPreviewLines(); } -var BRUSH_RADIUS = 5; +var BRUSH_RADIUS = 2; function mousePressEvent(event) { var mouseX = event.x; @@ -424,41 +424,60 @@ function mousePressEvent(event) { makeSphere(resultVoxel.x, resultVoxel.y, resultVoxel.z, voxelSize * BRUSH_RADIUS, voxelSize); } -var SAND_LIFETIME = 100.0; -var UPDATE_RATE = 1.0; - var sandArray = []; var numSand = 0; + //These arrays are used to buffer add/remove operations so they can be batched together var addArray = []; var addArraySize = 0; var removeArray = []; var removeArraySize = 0; -var red = 234; -var green = 206; -var blue = 106; +//The colors must be different +var activeSandColor = { r: 234, g: 206, b: 106}; +var inactiveSandColor = { r: 233, g: 0, b: 0}; + +//This is used as an optimization, so that we +//will check our 6 neighbors at most once. +var adjacentVoxels = []; +var numAdjacentVoxels = 0; +//Stores a list of voxels we need to activate +var activateMap = {}; function update() { + + activateMap = {}; + //Update all sand in our sandArray - for (var i = 0; i < numSand; i++) { - updateSand(i); + var i = 0; + while (i < numSand) { + //Update the sand voxel and if it doesn't move, deactivate it + if (updateSand(i) == false) { + deactivateSand(i); + } else { + i++; + } } - //Remove all voxels that need to be moved for (var i = 0; i < removeArraySize; i++) { - var elem = removeArray[i]; - Voxels.eraseVoxel(elem.x, elem.y, elem.z, elem.s); + var voxel = removeArray[i]; + Voxels.eraseVoxel(voxel.x, voxel.y, voxel.z, voxel.s); } removeArraySize = 0; - + //Add all voxels that have moved for (var i = 0; i < addArraySize; i++) { - var elem = addArray[i]; - Voxels.setVoxel(elem.x, elem.y, elem.z, elem.s, elem.r, elem.g, elem.b); + var voxel = addArray[i]; + Voxels.setVoxel(voxel.x, voxel.y, voxel.z, voxel.s, voxel.r, voxel.g, voxel.b); } addArraySize = 0; + + for (var key in activateMap) { + var voxel = activateMap[key]; + Voxels.setVoxel(voxel.x, voxel.y, voxel.z, voxel.s, 0, 0, 255); + sandArray[numSand++] = { x: voxel.x, y: voxel.y, z: voxel.z, s: voxel.s, r: activeSandColor.r, g: activeSandColor.g, b: activeSandColor.b }; + } } //Adds a sphere of sand at the center cx,cy,cz @@ -469,12 +488,8 @@ function makeSphere(cx, cy, cz, r, voxelSize) { var dx; var dy; var dz; - - Voxels.setVoxel(cx, cy, cz, voxelSize, red, green, blue); - sandArray[numSand] = { x: cx, y: cy, z: cz, s: voxelSize, r: red, g: green, b: blue }; - numSand++; - return 0; - + sandArray[numSand++] = { x: cx, y: cy, z: cz, s: voxelSize, r: activeSandColor.r, g: activeSandColor.g, b: activeSandColor.b }; + return for (var x = cx - r; x <= cx + r; x += voxelSize) { for (var y = cy - r; y <= cy + r; y += voxelSize) { for (var z = cz - r; z <= cz + r; z += voxelSize) { @@ -483,74 +498,115 @@ function makeSphere(cx, cy, cz, r, voxelSize) { dz = Math.abs(z - cz); distance2 = dx * dx + dy * dy + dz * dz; if (distance2 <= r2 && isVoxelEmpty(x, y, z, voxelSize)) { - Voxels.setVoxel(x, y, z, voxelSize, red, green, blue); - sandArray[numSand] = { x: x, y: y, z: z, s: voxelSize, r: red, g: green, b: blue }; - numSand++; + Voxels.setVoxel(x, y, z, voxelSize, activeSandColor.r, activeSandColor.g, activeSandColor.b); + sandArray[numSand++] = { x: x, y: y, z: z, s: voxelSize, r: activeSandColor.r, g: activeSandColor.g, b: activeSandColor.b }; } } } } } -function isVoxelEmpty(x, y, z, s) { +function isVoxelEmpty(x, y, z, s, isAdjacent) { var halfSize = s / 2; var point = {x: x + halfSize, y: y + halfSize, z: z + halfSize }; var adjacent = Voxels.getVoxelEnclosingPointBlocking(point); - //If color is all 0, we assume its air. - return (adjacent.red == 0 && adjacent.green == 0 && adjacent.blue == 0); + + if (adjacent.red == 0 && adjacent.green == 0 && adjacent.blue == 0) { + return true; + } + + if (isAdjacent) { + adjacentVoxels[numAdjacentVoxels++] = adjacent; + } + + return false; } function tryMoveVoxel(voxel, x, y, z) { //If the adjacent voxel is empty, we will move to it. - if (isVoxelEmpty(x, y, z, voxel.s)) { + if (isVoxelEmpty(x, y, z, voxel.s, false)) { moveVoxel(voxel, x, y, z); + var hsize = voxel.s / 2; + //Get all adjacent voxels for activation + for (var i = 0; i < 5; i++) { + var point = {x: voxel.x + directionVecs[i].x * voxel.s + hsize, y: voxel.y + directionVecs[i].y * voxel.s + hsize, z: voxel.z + directionVecs[i].z * voxel.s + hsize }; + adjacentVoxels[numAdjacentVoxels++] = Voxels.getVoxelEnclosingPointBlocking(point); + } return true; } return false; } function moveVoxel(voxel, x, y, z) { + activateNeighbors(); removeArray[removeArraySize++] = {x: voxel.x, y: voxel.y, z: voxel.z, s: voxel.s}; - addArray[addArraySize++] = {x: x, y: y, z: z, s: voxel.s, r: red, g: green, b: blue}; + addArray[addArraySize++] = {x: x, y: y, z: z, s: voxel.s, r: activeSandColor.r, g: activeSandColor.g, b: activeSandColor.b}; voxel.x = x; voxel.y = y; voxel.z = z; } +var LEFT = 0; +var BACK = 1; +var RIGHT = 2; +var FRONT = 3; +var TOP = 4; + +var directionVecs = []; +directionVecs[LEFT] = {x: -1, y: 0, z: 0}; //Left +directionVecs[BACK] = {x: 0, y: 0, z: -1}; //Back +directionVecs[RIGHT] = {x: 1, y: 0, z: 0}; //Right +directionVecs[FRONT] = {x: 0, y: 0, z: 1}; //Front +directionVecs[TOP] = {x: 0, y: 1, z: 0}; //Top + function updateSand(i) { var voxel = sandArray[i]; var size = voxel.s; + var hsize = size / 2; + numAdjacentVoxels = 0; //Down if (tryMoveVoxel(voxel, voxel.x, voxel.y - size, voxel.z)) { return true; } - return; - //Left - if (isVoxelEmpty(voxel.x - size, voxel.y, voxel.z, size) && isVoxelEmpty(voxel.x - size, voxel.y - size, voxel.z, size)) { - moveVoxel(voxel, voxel.x - size, voxel.y, voxel.z); - return true; - } - //Back - if (isVoxelEmpty(voxel.x, voxel.y, voxel.z - size, size) && isVoxelEmpty(voxel.x, voxel.y - size, voxel.z - size, size)) { - moveVoxel(voxel, voxel.x, voxel.y, voxel.z - size); - return true; - } - //Right - if (isVoxelEmpty(voxel.x + size, voxel.y, voxel.z, size) && isVoxelEmpty(voxel.x + size, voxel.y - size, voxel.z, size)) { - moveVoxel(voxel, voxel.x + size, voxel.y, voxel.z); - return true; - } - //Front - if (isVoxelEmpty(voxel.x, voxel.y, voxel.z + size, size) && isVoxelEmpty(voxel.x, voxel.y - size, voxel.z + size, size)) { - moveVoxel(voxel, voxel.x, voxel.y, voxel.z + size); - return true; + + //Left, back, right, front + for (var i = 0; i < 4; i++) { + if (isVoxelEmpty(voxel.x + directionVecs[i].x * size, voxel.y + directionVecs[i].y * size, voxel.z + directionVecs[i].z * size, size, true) + && isVoxelEmpty(voxel.x + directionVecs[i].x * size, (voxel.y - size) + directionVecs[i].y * size, voxel.z + directionVecs[i].z * size, size, false)) { + //get the rest of the adjacent voxels + for (var j = i + 1; j < 5; j++) { + var point = {x: voxel.x + directionVecs[j].x * size + hsize, y: voxel.y + directionVecs[j].y * size + hsize, z: voxel.z + directionVecs[j].z * size + hsize }; + adjacentVoxels[numAdjacentVoxels++] = Voxels.getVoxelEnclosingPointBlocking(point); + } + moveVoxel(voxel, voxel.x + directionVecs[i].x * size, voxel.y + directionVecs[i].y * size, voxel.z + directionVecs[i].z * size); + return true; + } } + return false; } +function activateNeighbors() { + for (var i = 0; i < numAdjacentVoxels; i++) { + var voxel = adjacentVoxels[i]; + //Check if this neighbor is inactive, if so, activate it + if (voxel.red == inactiveSandColor.r && voxel.green == inactiveSandColor.g && voxel.blue == inactiveSandColor.b) { + print("Activating"); + activateMap[voxel.x.toString() + "," + voxel.y.toString() + ',' + voxel.z.toString()] = voxel; + } + } +} + +function deactivateSand(i) { + var voxel = sandArray[i]; + addArray[addArraySize++] = {x: voxel.x, y: voxel.y, z: voxel.z, s: voxel.s, r: inactiveSandColor.r, g: inactiveSandColor.g, b: inactiveSandColor.b}; + sandArray[i] = sandArray[numSand-1]; + numSand--; +} + function scriptEnding() { for (var i = 0; i < numSand; i++) { var voxel = sandArray[i]; @@ -570,4 +626,5 @@ Controller.mouseMoveEvent.connect(mouseMoveEvent); Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); +Voxels.setMaxPacketSize(1); //this is needed or a bug occurs :( Voxels.setPacketsPerSecond(10000); \ No newline at end of file From 8b911c3e2c75da63fd6f635a46f80bd12b5badb3 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 16 Jul 2014 11:21:58 -0700 Subject: [PATCH 6/7] Finished up basic falling sand example script --- examples/fallingSand.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/examples/fallingSand.js b/examples/fallingSand.js index 95f1bf3764..10b02e32c7 100644 --- a/examples/fallingSand.js +++ b/examples/fallingSand.js @@ -436,7 +436,7 @@ var removeArraySize = 0; //The colors must be different var activeSandColor = { r: 234, g: 206, b: 106}; -var inactiveSandColor = { r: 233, g: 0, b: 0}; +var inactiveSandColor = { r: 233, g: 206, b: 106}; //This is used as an optimization, so that we //will check our 6 neighbors at most once. @@ -447,6 +447,7 @@ var activateMap = {}; function update() { + //Clear the activate map each frame activateMap = {}; //Update all sand in our sandArray @@ -475,7 +476,7 @@ function update() { for (var key in activateMap) { var voxel = activateMap[key]; - Voxels.setVoxel(voxel.x, voxel.y, voxel.z, voxel.s, 0, 0, 255); + Voxels.setVoxel(voxel.x, voxel.y, voxel.z, voxel.s, activeSandColor.r, activeSandColor.g, activeSandColor.b); sandArray[numSand++] = { x: voxel.x, y: voxel.y, z: voxel.z, s: voxel.s, r: activeSandColor.r, g: activeSandColor.g, b: activeSandColor.b }; } } @@ -488,8 +489,7 @@ function makeSphere(cx, cy, cz, r, voxelSize) { var dx; var dy; var dz; - sandArray[numSand++] = { x: cx, y: cy, z: cz, s: voxelSize, r: activeSandColor.r, g: activeSandColor.g, b: activeSandColor.b }; - return + for (var x = cx - r; x <= cx + r; x += voxelSize) { for (var y = cy - r; y <= cy + r; y += voxelSize) { for (var z = cz - r; z <= cz + r; z += voxelSize) { @@ -506,6 +506,7 @@ function makeSphere(cx, cy, cz, r, voxelSize) { } } +//Check if a given voxel is empty function isVoxelEmpty(x, y, z, s, isAdjacent) { var halfSize = s / 2; var point = {x: x + halfSize, y: y + halfSize, z: z + halfSize }; @@ -524,21 +525,24 @@ function isVoxelEmpty(x, y, z, s, isAdjacent) { return false; } +//Moves voxel to x,y,z if the space is empty function tryMoveVoxel(voxel, x, y, z) { //If the adjacent voxel is empty, we will move to it. if (isVoxelEmpty(x, y, z, voxel.s, false)) { - moveVoxel(voxel, x, y, z); var hsize = voxel.s / 2; - //Get all adjacent voxels for activation for (var i = 0; i < 5; i++) { var point = {x: voxel.x + directionVecs[i].x * voxel.s + hsize, y: voxel.y + directionVecs[i].y * voxel.s + hsize, z: voxel.z + directionVecs[i].z * voxel.s + hsize }; adjacentVoxels[numAdjacentVoxels++] = Voxels.getVoxelEnclosingPointBlocking(point); } + moveVoxel(voxel, x, y, z); + + //Get all adjacent voxels for activation return true; } return false; } +//Moves voxel to x,y,z function moveVoxel(voxel, x, y, z) { activateNeighbors(); removeArray[removeArraySize++] = {x: voxel.x, y: voxel.y, z: voxel.z, s: voxel.s}; @@ -554,6 +558,7 @@ var RIGHT = 2; var FRONT = 3; var TOP = 4; +//These indicate the different directions to neighbor voxels, so we can iterate them var directionVecs = []; directionVecs[LEFT] = {x: -1, y: 0, z: 0}; //Left directionVecs[BACK] = {x: 0, y: 0, z: -1}; //Back @@ -594,12 +599,12 @@ function activateNeighbors() { var voxel = adjacentVoxels[i]; //Check if this neighbor is inactive, if so, activate it if (voxel.red == inactiveSandColor.r && voxel.green == inactiveSandColor.g && voxel.blue == inactiveSandColor.b) { - print("Activating"); activateMap[voxel.x.toString() + "," + voxel.y.toString() + ',' + voxel.z.toString()] = voxel; } } } +//Deactivates a sand voxel to save processing power function deactivateSand(i) { var voxel = sandArray[i]; addArray[addArraySize++] = {x: voxel.x, y: voxel.y, z: voxel.z, s: voxel.s, r: inactiveSandColor.r, g: inactiveSandColor.g, b: inactiveSandColor.b}; @@ -607,6 +612,7 @@ function deactivateSand(i) { numSand--; } +//Cleanup function scriptEnding() { for (var i = 0; i < numSand; i++) { var voxel = sandArray[i]; From 77b2dd695d983033ed07ace5df553c8310192fb2 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 16 Jul 2014 11:34:31 -0700 Subject: [PATCH 7/7] fixed header --- examples/fallingSand.js | 3 ++- examples/grenadeLauncher.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/fallingSand.js b/examples/fallingSand.js index 10b02e32c7..ba8dd703fb 100644 --- a/examples/fallingSand.js +++ b/examples/fallingSand.js @@ -5,7 +5,8 @@ // Created by Ben Arnold on 7/14/14. // Copyright 2014 High Fidelity, Inc. // -// This sample script allows the user to grow different types of plants on the voxels +// This sample script allows the user to place sand voxels that will undergo +// cellular automata physics. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/examples/grenadeLauncher.js b/examples/grenadeLauncher.js index f20e915f07..0cac55baa7 100644 --- a/examples/grenadeLauncher.js +++ b/examples/grenadeLauncher.js @@ -8,7 +8,7 @@ // // This is an example script that turns the hydra controllers and mouse into a particle gun. // It reads the controller, watches for trigger pulls, and launches particles. -// When particles collide with voxels they blow little holes out of the voxels. +// When particles collide with voxels they blow big holes out of the voxels. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html