Merge pull request #3175 from Barnold1953/OculusSDK

Two new example scripts
This commit is contained in:
Brad Hefta-Gaub 2014-07-16 12:02:37 -07:00
commit 3dfb5030c5
4 changed files with 1017 additions and 0 deletions

637
examples/fallingSand.js Normal file
View file

@ -0,0 +1,637 @@
//
// fallingSand.js
// examples
//
// Created by Ben Arnold on 7/14/14.
// Copyright 2014 High Fidelity, Inc.
//
// 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
//
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 = 2;
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 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;
//The colors must be different
var activeSandColor = { r: 234, g: 206, b: 106};
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.
var adjacentVoxels = [];
var numAdjacentVoxels = 0;
//Stores a list of voxels we need to activate
var activateMap = {};
function update() {
//Clear the activate map each frame
activateMap = {};
//Update all sand in our sandArray
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++;
}
}
for (var i = 0; i < removeArraySize; i++) {
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 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, 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 };
}
}
//Adds a sphere of sand at the center cx,cy,cz
function makeSphere(cx, cy, cz, r, voxelSize) {
var r2 = r * r;
var distance2;
var dx;
var dy;
var dz;
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, 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 };
}
}
}
}
}
//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 };
var adjacent = Voxels.getVoxelEnclosingPointBlocking(point);
//If color is all 0, we assume its air.
if (adjacent.red == 0 && adjacent.green == 0 && adjacent.blue == 0) {
return true;
}
if (isAdjacent) {
adjacentVoxels[numAdjacentVoxels++] = adjacent;
}
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)) {
var hsize = voxel.s / 2;
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};
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;
//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
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;
}
//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) {
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};
sandArray[i] = sandArray[numSand-1];
numSand--;
}
//Cleanup
function scriptEnding() {
for (var i = 0; i < numSand; i++) {
var voxel = sandArray[i];
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.setMaxPacketSize(1); //this is needed or a bug occurs :(
Voxels.setPacketsPerSecond(10000);

358
examples/grenadeLauncher.js Normal file
View file

@ -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 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
//
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);

View file

@ -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<VoxelTreeElement*>(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;
}

View file

@ -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);