mirror of
https://github.com/lubosz/overte.git
synced 2025-04-07 19:22:39 +02:00
add some new tutorial JS files
This commit is contained in:
parent
3a2cb37ce2
commit
50117e2ff4
13 changed files with 1613 additions and 0 deletions
51
scripts/default/html/spinButtons.js
Normal file
51
scripts/default/html/spinButtons.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// spinButtons.js
|
||||
//
|
||||
// Created by David Rowe on 20 Apr 2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function hoverSpinButtons(event) {
|
||||
var input = event.target,
|
||||
x = event.offsetX,
|
||||
y = event.offsetY,
|
||||
width = input.offsetWidth,
|
||||
height = input.offsetHeight,
|
||||
SPIN_WIDTH = 11,
|
||||
SPIN_MARGIN = 2,
|
||||
maxX = width - SPIN_MARGIN,
|
||||
minX = maxX - SPIN_WIDTH;
|
||||
|
||||
if (minX <= x && x <= maxX) {
|
||||
if (y < height / 2) {
|
||||
input.classList.remove("hover-down");
|
||||
input.classList.add("hover-up");
|
||||
} else {
|
||||
input.classList.remove("hover-up");
|
||||
input.classList.add("hover-down");
|
||||
}
|
||||
} else {
|
||||
input.classList.remove("hover-up");
|
||||
input.classList.remove("hover-down");
|
||||
}
|
||||
}
|
||||
|
||||
function unhoverSpinButtons(event) {
|
||||
event.target.classList.remove("hover-up");
|
||||
event.target.classList.remove("hover-down");
|
||||
}
|
||||
|
||||
function augmentSpinButtons() {
|
||||
var inputs, i, length;
|
||||
|
||||
inputs = document.getElementsByTagName("INPUT");
|
||||
for (i = 0, length = inputs.length; i < length; i += 1) {
|
||||
if (inputs[i].type === "number") {
|
||||
inputs[i].addEventListener("mousemove", hoverSpinButtons);
|
||||
inputs[i].addEventListener("mouseout", unhoverSpinButtons);
|
||||
}
|
||||
}
|
||||
}
|
313
scripts/developer/libraries/utils.js
Normal file
313
scripts/developer/libraries/utils.js
Normal file
|
@ -0,0 +1,313 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/08/29
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
vec3toStr = function(v, digits) {
|
||||
if (!digits) { digits = 3; }
|
||||
return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }";
|
||||
}
|
||||
|
||||
quatToStr = function(q, digits) {
|
||||
if (!digits) { digits = 3; }
|
||||
return "{ " + q.w.toFixed(digits) + ", " + q.x.toFixed(digits) + ", " +
|
||||
q.y.toFixed(digits) + ", " + q.z.toFixed(digits)+ " }";
|
||||
}
|
||||
|
||||
vec3equal = function(v0, v1) {
|
||||
return (v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z);
|
||||
}
|
||||
|
||||
colorMix = function(colorA, colorB, mix) {
|
||||
var result = {};
|
||||
for (var key in colorA) {
|
||||
result[key] = (colorA[key] * (1 - mix)) + (colorB[key] * mix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
scaleLine = function (start, end, scale) {
|
||||
var v = Vec3.subtract(end, start);
|
||||
var length = Vec3.length(v);
|
||||
v = Vec3.multiply(scale, v);
|
||||
return Vec3.sum(start, v);
|
||||
}
|
||||
|
||||
findAction = function(name) {
|
||||
return Controller.findAction(name);
|
||||
}
|
||||
|
||||
addLine = function(origin, vector, color) {
|
||||
if (!color) {
|
||||
color = COLORS.WHITE
|
||||
}
|
||||
return Entities.addEntity(mergeObjects(LINE_PROTOTYPE, {
|
||||
position: origin,
|
||||
linePoints: [
|
||||
ZERO_VECTOR,
|
||||
vector,
|
||||
],
|
||||
color: color
|
||||
}));
|
||||
}
|
||||
|
||||
// FIXME fetch from a subkey of user data to support non-destructive modifications
|
||||
setEntityUserData = function(id, data) {
|
||||
var json = JSON.stringify(data)
|
||||
Entities.editEntity(id, { userData: json });
|
||||
}
|
||||
|
||||
// FIXME do non-destructive modification of the existing user data
|
||||
getEntityUserData = function(id) {
|
||||
var results = null;
|
||||
var properties = Entities.getEntityProperties(id, "userData");
|
||||
if (properties.userData) {
|
||||
try {
|
||||
results = JSON.parse(properties.userData);
|
||||
} catch(err) {
|
||||
logDebug(err);
|
||||
logDebug(properties.userData);
|
||||
}
|
||||
}
|
||||
return results ? results : {};
|
||||
}
|
||||
|
||||
|
||||
// Non-destructively modify the user data of an entity.
|
||||
setEntityCustomData = function(customKey, id, data) {
|
||||
var userData = getEntityUserData(id);
|
||||
if (data == null) {
|
||||
delete userData[customKey];
|
||||
} else {
|
||||
userData[customKey] = data;
|
||||
}
|
||||
setEntityUserData(id, userData);
|
||||
}
|
||||
|
||||
getEntityCustomData = function(customKey, id, defaultValue) {
|
||||
var userData = getEntityUserData(id);
|
||||
if (undefined != userData[customKey]) {
|
||||
return userData[customKey];
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
mergeObjects = function(proto, custom) {
|
||||
var result = {};
|
||||
for (var attrname in proto) {
|
||||
result[attrname] = proto[attrname];
|
||||
}
|
||||
for (var attrname in custom) {
|
||||
result[attrname] = custom[attrname];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LOG_WARN = 1;
|
||||
|
||||
logWarn = function(str) {
|
||||
if (LOG_WARN) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR = 1;
|
||||
|
||||
logError = function(str) {
|
||||
if (LOG_ERROR) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO = 1;
|
||||
|
||||
logInfo = function(str) {
|
||||
if (LOG_INFO) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG = 0;
|
||||
|
||||
logDebug = function(str) {
|
||||
if (LOG_DEBUG) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_TRACE = 0;
|
||||
|
||||
logTrace = function(str) {
|
||||
if (LOG_TRACE) {
|
||||
print(str);
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the penetration between a point and a sphere (centered at the origin)
|
||||
// if point is inside sphere: returns true and stores the result in 'penetration'
|
||||
// (the vector that would move the point outside the sphere)
|
||||
// otherwise returns false
|
||||
findSphereHit = function(point, sphereRadius) {
|
||||
var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations
|
||||
var vectorLength = Vec3.length(point);
|
||||
if (vectorLength < EPSILON) {
|
||||
return true;
|
||||
}
|
||||
var distance = vectorLength - sphereRadius;
|
||||
if (distance < 0.0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
findSpherePointHit = function(sphereCenter, sphereRadius, point) {
|
||||
return findSphereHit(Vec3.subtract(point,sphereCenter), sphereRadius);
|
||||
}
|
||||
|
||||
findSphereSphereHit = function(firstCenter, firstRadius, secondCenter, secondRadius) {
|
||||
return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter);
|
||||
}
|
||||
|
||||
// Given a vec3 v, return a vec3 that is the same vector relative to the avatars
|
||||
// DEFAULT eye position, rotated into the avatars reference frame.
|
||||
getEyeRelativePosition = function(v) {
|
||||
return Vec3.sum(MyAvatar.getDefaultEyePosition(), Vec3.multiplyQbyV(MyAvatar.orientation, v));
|
||||
}
|
||||
|
||||
getAvatarRelativeRotation = function(q) {
|
||||
return Quat.multiply(MyAvatar.orientation, q);
|
||||
}
|
||||
|
||||
pointInExtents = function(point, minPoint, maxPoint) {
|
||||
return (point.x >= minPoint.x && point.x <= maxPoint.x) &&
|
||||
(point.y >= minPoint.y && point.y <= maxPoint.y) &&
|
||||
(point.z >= minPoint.z && point.z <= maxPoint.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an HSL color value to RGB. Conversion formula
|
||||
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||
* returns r, g, and b in the set [0, 255].
|
||||
*
|
||||
* @param Number h The hue
|
||||
* @param Number s The saturation
|
||||
* @param Number l The lightness
|
||||
* @return Array The RGB representation
|
||||
*/
|
||||
hslToRgb = function(hsl) {
|
||||
var r, g, b;
|
||||
if (hsl.s == 0) {
|
||||
r = g = b = hsl.l; // achromatic
|
||||
} else {
|
||||
var hue2rgb = function hue2rgb(p, q, t) {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
var q = hsl.l < 0.5 ? hsl.l * (1 + hsl.s) : hsl.l + hsl.s - hsl.l * hsl.s;
|
||||
var p = 2 * hsl.l - q;
|
||||
r = hue2rgb(p, q, hsl.h + 1 / 3);
|
||||
g = hue2rgb(p, q, hsl.h);
|
||||
b = hue2rgb(p, q, hsl.h - 1 / 3);
|
||||
}
|
||||
|
||||
return {
|
||||
red: Math.round(r * 255),
|
||||
green: Math.round(g * 255),
|
||||
blue: Math.round(b * 255)
|
||||
};
|
||||
}
|
||||
|
||||
map = function(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
orientationOf = function(vector) {
|
||||
var Y_AXIS = {
|
||||
x: 0,
|
||||
y: 1,
|
||||
z: 0
|
||||
};
|
||||
var X_AXIS = {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var theta = 0.0;
|
||||
|
||||
var RAD_TO_DEG = 180.0 / Math.PI;
|
||||
var direction, yaw, pitch;
|
||||
direction = Vec3.normalize(vector);
|
||||
yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
|
||||
pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
|
||||
return Quat.multiply(yaw, pitch);
|
||||
}
|
||||
|
||||
randFloat = function(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
||||
|
||||
randInt = function(low, high) {
|
||||
return Math.floor(randFloat(low, high));
|
||||
}
|
||||
|
||||
|
||||
randomColor = function() {
|
||||
return {
|
||||
red: randInt(0, 255),
|
||||
green: randInt(0, 255),
|
||||
blue: randInt(0, 255)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hexToRgb = function(hex) {
|
||||
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ? {
|
||||
red: parseInt(result[1], 16),
|
||||
green: parseInt(result[2], 16),
|
||||
blue: parseInt(result[3], 16)
|
||||
} : null;
|
||||
}
|
||||
|
||||
calculateHandSizeRatio = function() {
|
||||
// Get the ratio of the current avatar's hand to Owen's hand
|
||||
|
||||
var standardCenterHandPoint = 0.11288;
|
||||
var jointNames = MyAvatar.getJointNames();
|
||||
//get distance from handJoint up to leftHandIndex3 as a proxy for center of hand
|
||||
var wristToFingertipDistance = 0;;
|
||||
for (var i = 0; i < jointNames.length; i++) {
|
||||
var jointName = jointNames[i];
|
||||
print(jointName)
|
||||
if (jointName.indexOf("LeftHandIndex") !== -1) {
|
||||
// translations are relative to parent joint, so simply add them together
|
||||
// joints face down the y-axis
|
||||
var translation = MyAvatar.getDefaultJointTranslation(i).y;
|
||||
wristToFingertipDistance += translation;
|
||||
}
|
||||
}
|
||||
// Right now units are in cm, so convert to meters
|
||||
wristToFingertipDistance /= 100;
|
||||
|
||||
var centerHandPoint = wristToFingertipDistance/2;
|
||||
|
||||
// Compare against standard hand (Owen)
|
||||
var handSizeRatio = centerHandPoint/standardCenterHandPoint;
|
||||
return handSizeRatio;
|
||||
}
|
||||
|
||||
clamp = function(val, min, max){
|
||||
return Math.max(min, Math.min(max, val))
|
||||
}
|
||||
|
176
scripts/developer/utilities/tests/editEntityStressTest.js
Normal file
176
scripts/developer/utilities/tests/editEntityStressTest.js
Normal file
|
@ -0,0 +1,176 @@
|
|||
// entityEditStressTest.js
|
||||
//
|
||||
// Created by Seiji Emery on 8/31/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Stress tests the client + server-side entity trees by spawning huge numbers of entities in
|
||||
// close proximity to your avatar and updating them continuously (ie. applying position edits),
|
||||
// with the intent of discovering crashes and other bugs related to the entity, scripting,
|
||||
// rendering, networking, and/or physics subsystems.
|
||||
//
|
||||
// This script was originally created to find + diagnose an a clientside crash caused by improper
|
||||
// locking of the entity tree, but can be reused for other purposes.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var NUM_ENTITIES = 20000; // number of entities to spawn
|
||||
var ENTITY_SPAWN_LIMIT = 1000;
|
||||
var ENTITY_SPAWN_INTERVAL = 0.1;
|
||||
|
||||
var UPDATE_INTERVAL = 0.05; // Re-randomize the entity's position every x seconds / ms
|
||||
var ENTITY_LIFETIME = 30; // Entity timeout (when/if we crash, we need the entities to delete themselves)
|
||||
var KEEPALIVE_INTERVAL = 5; // Refreshes the timeout every X seconds
|
||||
|
||||
var RADIUS = 5.0; // Spawn within this radius (square)
|
||||
var Y_OFFSET = 1.5; // Spawn at an offset below the avatar
|
||||
var TEST_ENTITY_NAME = "EntitySpawnTest";
|
||||
|
||||
(function () {
|
||||
this.makeEntity = function (properties) {
|
||||
var entity = Entities.addEntity(properties);
|
||||
// print("spawning entity: " + JSON.stringify(properties));
|
||||
|
||||
return {
|
||||
update: function (properties) {
|
||||
Entities.editEntity(entity, properties);
|
||||
},
|
||||
destroy: function () {
|
||||
Entities.deleteEntity(entity)
|
||||
},
|
||||
getAge: function () {
|
||||
return Entities.getEntityProperties(entity).age;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.randomPositionXZ = function (center, radius) {
|
||||
return {
|
||||
x: center.x + (Math.random() * radius * 2.0) - radius,
|
||||
y: center.y,
|
||||
z: center.z + (Math.random() * radius * 2.0) - radius
|
||||
};
|
||||
}
|
||||
this.randomColor = function () {
|
||||
var shade = Math.floor(Math.random() * 255);
|
||||
var hue = Math.floor(Math.random() * (255 - shade));
|
||||
|
||||
return {
|
||||
red: shade + hue,
|
||||
green: shade,
|
||||
blue: shade
|
||||
};
|
||||
}
|
||||
this.randomDimensions = function () {
|
||||
return {
|
||||
x: 0.1 + Math.random() * 0.5,
|
||||
y: 0.1 + Math.random() * 0.1,
|
||||
z: 0.1 + Math.random() * 0.5
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
(function () {
|
||||
var entities = [];
|
||||
var entitiesToCreate = 0;
|
||||
var entitiesSpawned = 0;
|
||||
|
||||
|
||||
function clear () {
|
||||
var ids = Entities.findEntities(MyAvatar.position, 50);
|
||||
var that = this;
|
||||
ids.forEach(function(id) {
|
||||
var properties = Entities.getEntityProperties(id);
|
||||
if (properties.name == TEST_ENTITY_NAME) {
|
||||
Entities.deleteEntity(id);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
function createEntities () {
|
||||
print("Creating " + NUM_ENTITIES + " entities (UPDATE_INTERVAL = " + UPDATE_INTERVAL + ", KEEPALIVE_INTERVAL = " + KEEPALIVE_INTERVAL + ")");
|
||||
entitiesToCreate = NUM_ENTITIES;
|
||||
Script.update.connect(spawnEntities);
|
||||
}
|
||||
|
||||
var spawnTimer = 0.0;
|
||||
function spawnEntities (dt) {
|
||||
if (entitiesToCreate <= 0) {
|
||||
Script.update.disconnect(spawnEntities);
|
||||
print("Finished spawning entities");
|
||||
}
|
||||
else if ((spawnTimer -= dt) < 0.0){
|
||||
spawnTimer = ENTITY_SPAWN_INTERVAL;
|
||||
|
||||
var n = Math.min(entitiesToCreate, ENTITY_SPAWN_LIMIT);
|
||||
print("Spawning " + n + " entities (" + (entitiesSpawned += n) + ")");
|
||||
|
||||
|
||||
entitiesToCreate -= n;
|
||||
|
||||
var center = MyAvatar.position;
|
||||
center.y -= Y_OFFSET;
|
||||
|
||||
for (; n > 0; --n) {
|
||||
entities.push(makeEntity({
|
||||
type: "Box",
|
||||
name: TEST_ENTITY_NAME,
|
||||
position: randomPositionXZ(center, RADIUS),
|
||||
color: randomColor(),
|
||||
dimensions: randomDimensions(),
|
||||
lifetime: ENTITY_LIFETIME
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function despawnEntities () {
|
||||
print("despawning entities");
|
||||
entities.forEach(function (entity) {
|
||||
entity.destroy();
|
||||
});
|
||||
entities = [];
|
||||
}
|
||||
|
||||
var keepAliveTimer = 0.0;
|
||||
var updateTimer = 0.0;
|
||||
|
||||
// Runs the following entity updates:
|
||||
// a) refreshes the timeout interval every KEEPALIVE_INTERVAL seconds, and
|
||||
// b) re-randomizes its position every UPDATE_INTERVAL seconds.
|
||||
// This should be sufficient to crash the client until the entity tree bug is fixed (and thereafter if it shows up again).
|
||||
function updateEntities (dt) {
|
||||
var updateLifetime = ((keepAliveTimer -= dt) < 0.0) ? ((keepAliveTimer = KEEPALIVE_INTERVAL), true) : false;
|
||||
var updateProperties = ((updateTimer -= dt) < 0.0) ? ((updateTimer = UPDATE_INTERVAL), true) : false;
|
||||
|
||||
if (updateLifetime || updateProperties) {
|
||||
var center = MyAvatar.position;
|
||||
center.y -= Y_OFFSET;
|
||||
|
||||
entities.forEach((updateLifetime && updateProperties && function (entity) {
|
||||
entity.update({
|
||||
lifetime: entity.getAge() + ENTITY_LIFETIME,
|
||||
position: randomPositionXZ(center, RADIUS)
|
||||
});
|
||||
}) || (updateLifetime && function (entity) {
|
||||
entity.update({
|
||||
lifetime: entity.getAge() + ENTITY_LIFETIME
|
||||
});
|
||||
}) || (updateProperties && function (entity) {
|
||||
entity.update({
|
||||
position: randomPositionXZ(center, RADIUS)
|
||||
});
|
||||
}) || null, this);
|
||||
}
|
||||
}
|
||||
|
||||
function init () {
|
||||
Script.update.disconnect(init);
|
||||
clear();
|
||||
createEntities();
|
||||
Script.update.connect(updateEntities);
|
||||
Script.scriptEnding.connect(despawnEntities);
|
||||
}
|
||||
Script.update.connect(init);
|
||||
})();
|
47
scripts/tutorials/createFlashlight.js
Normal file
47
scripts/tutorials/createFlashlight.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// Created by Sam Gateau on 9/9/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is a toy script that create a flashlight entity that lit when grabbed
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
var SCRIPT_URL = "http://hifi-production.s3.amazonaws.com/tutorials/entity_scripts/flashlight.js";
|
||||
var MODEL_URL = "http://hifi-production.s3.amazonaws.com/tutorials/flashlight/flashlight2.fbx";
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var flashlight = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: 'Tutorial Flashlight',
|
||||
modelURL: MODEL_URL,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.08,
|
||||
y: 0.30,
|
||||
z: 0.08
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -5.0,
|
||||
z: 0
|
||||
},
|
||||
dynamic: true,
|
||||
shapeType: 'box',
|
||||
lifetime: 3600,
|
||||
script: SCRIPT_URL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Script.stop();
|
87
scripts/tutorials/createGolfClub.js
Normal file
87
scripts/tutorials/createGolfClub.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// golfClub.js
|
||||
//
|
||||
// Created by Philip Rosedale on April 11, 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// A simple golf club. If you have equipped it, and pull trigger, it will either make
|
||||
// you a new golf ball, or take you to your ball if one is not made.
|
||||
|
||||
var orientation = MyAvatar.orientation;
|
||||
orientation = Quat.safeEulerAngles(orientation);
|
||||
orientation.x = 0;
|
||||
orientation = Quat.fromVec3Degrees(orientation);
|
||||
var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getFront(orientation)));
|
||||
|
||||
var CLUB_MODEL = "http://hifi-production.s3.amazonaws.com/tutorials/golfClub/putter_VR.fbx";
|
||||
var CLUB_COLLISION_HULL = "http://hifi-production.s3.amazonaws.com/tutorials/golfClub/club_collision_hull.obj";
|
||||
|
||||
var CLUB_DIMENSIONS = {
|
||||
"x": 0.043093059211969376,
|
||||
"y": 1.1488667726516724,
|
||||
"z": 0.42455694079399109
|
||||
};
|
||||
|
||||
var CLUB_ROTATION = {
|
||||
"w": 0.41972994804382324,
|
||||
"x": 0.78570234775543213,
|
||||
"y": -0.41875332593917847,
|
||||
"z": 0.17653167247772217
|
||||
};
|
||||
|
||||
|
||||
var SCRIPT_URL = "http://hifi-production.s3.amazonaws.com/tutorials/entity_scripts/golfClub.js";
|
||||
var golfClubProperties = {
|
||||
position: center,
|
||||
lifetime: 3600,
|
||||
collisionsWillMove: true,
|
||||
compoundShapeURL: CLUB_COLLISION_HULL,
|
||||
description: "Spawns ball or jumps to ball with trigger",
|
||||
dimensions: CLUB_DIMENSIONS,
|
||||
dynamic: true,
|
||||
modelURL: CLUB_MODEL,
|
||||
name: "Tutorial Golf Putter",
|
||||
script: SCRIPT_URL,
|
||||
shapeType: "compound",
|
||||
type: "Model",
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -5.0,
|
||||
z: 0
|
||||
},
|
||||
userData: JSON.stringify({
|
||||
wearable: {
|
||||
joints: {
|
||||
LeftHand: [{
|
||||
x: -0.1631782054901123,
|
||||
y: 0.44648152589797974,
|
||||
z: 0.10100018978118896
|
||||
}, {
|
||||
x: -0.9181621670722961,
|
||||
y: -0.0772884339094162,
|
||||
z: -0.3870723247528076,
|
||||
w: -0.0343472845852375
|
||||
}],
|
||||
RightHand: [{
|
||||
x: 0.16826771199703217,
|
||||
y: 0.4757269620895386,
|
||||
z: 0.07139724493026733
|
||||
}, {
|
||||
x: -0.7976328134536743,
|
||||
y: -0.0011603273451328278,
|
||||
z: 0.6030101776123047,
|
||||
w: -0.012610925361514091
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
var golfClub = Entities.addEntity(golfClubProperties);
|
||||
|
||||
Script.stop();
|
95
scripts/tutorials/createPictureFrame.js
Normal file
95
scripts/tutorials/createPictureFrame.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// Created by James B. Pollack @imgntn on April 18, 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This script shows how to create an entity with a picture texture on it that you can change either in script or in the entity's textures property.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// familiar code to put the entity in front of us
|
||||
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(1, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
// this is just a model exported from blender with a texture named 'Picture' on one face. also made it emissive so it doesn't require lighting.
|
||||
var MODEL_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/finalFrame.fbx";
|
||||
|
||||
//this is where we are going to get our image from. the stuff at the end is our API key.
|
||||
var NASA_API_ENDPOINT = "https://api.nasa.gov/planetary/apod?api_key=XNmgPJvVK8hGroZHB19PaQtlqKZk4q8GorWViuND";
|
||||
|
||||
//actually go get the data and return it
|
||||
function getDataFromNASA() {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("GET", NASA_API_ENDPOINT, false);
|
||||
request.send();
|
||||
|
||||
var response = JSON.parse(request.responseText);
|
||||
return response;
|
||||
};
|
||||
|
||||
//make the picture frame and set its texture url to the picture of the day from NASA
|
||||
function makePictureFrame() {
|
||||
// Calculate rotation necessary to face picture towards user at spawn time.
|
||||
var rotation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, 180, 0), Camera.getOrientation());
|
||||
rotation.x = 0;
|
||||
rotation.z = 0;
|
||||
var data = getDataFromNASA();
|
||||
var pictureFrameProperties = {
|
||||
name: 'Tutorial Picture Frame',
|
||||
description: data.explanation,
|
||||
type: 'Model',
|
||||
dimensions: {
|
||||
x: 1.2,
|
||||
y: 0.9,
|
||||
z: 0.075
|
||||
},
|
||||
position: center,
|
||||
rotation: rotation,
|
||||
textures: JSON.stringify({
|
||||
Picture: data.url
|
||||
}),
|
||||
modelURL: MODEL_URL,
|
||||
lifetime: 3600,
|
||||
dynamic: true,
|
||||
}
|
||||
var pictureFrame = Entities.addEntity(pictureFrameProperties);
|
||||
|
||||
var OUTER_FRAME_MODEL_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/outer_frame.fbx";
|
||||
var outerFrameProps = {
|
||||
name: "Tutorial Outer Frame",
|
||||
type: "Model",
|
||||
position: center,
|
||||
rotation: rotation,
|
||||
modelURL: OUTER_FRAME_MODEL_URL,
|
||||
lifetime: 3600,
|
||||
dynamic: true,
|
||||
dimensions: {
|
||||
x: 1.4329,
|
||||
y: 1.1308,
|
||||
z: 0.0464
|
||||
},
|
||||
parentID: pictureFrame // A parentd object will move, rotate, and scale with its parent.
|
||||
}
|
||||
var outerFrame = Entities.addEntity(outerFrameProps);
|
||||
Script.stop();
|
||||
}
|
||||
|
||||
|
||||
makePictureFrame();
|
||||
|
||||
//the data the NASA API returns looks like this:
|
||||
//
|
||||
// {
|
||||
// date: "2016-04-18",
|
||||
// explanation: "The International Space Station is the largest object ever constructed by humans in space. The station perimeter extends over roughly the area of a football field, although only a small fraction of this is composed of modules habitable by humans. The station is so large that it could not be launched all at once -- it continues to be built piecemeal. To function, the ISS needs huge trusses, some over 15 meters long and with masses over 10,000 kilograms, to keep it rigid and to route electricity and liquid coolants. Pictured above, the immense space station was photographed from the now-retired space shuttle Atlantis after a week-long stay in 2010. Across the image top hangs part of a bright blue Earth, in stark contrast to the darkness of interstellar space across the bottom.",
|
||||
// hdurl: "http://apod.nasa.gov/apod/image/1604/ISS02_NASA_4288.jpg",
|
||||
// media_type: "image",
|
||||
// service_version: "v1",
|
||||
// title: "The International Space Station over Earth",
|
||||
// url: "http://apod.nasa.gov/apod/image/1604/ISS02_NASA_960.jpg"
|
||||
// }
|
73
scripts/tutorials/createPingPongGun.js
Normal file
73
scripts/tutorials/createPingPongGun.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// Created by James B. Pollack @imgntn on April 18, 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var SCRIPT_URL = "http://hifi-production.s3.amazonaws.com/tutorials/entity_scripts/pingPongGun.js";
|
||||
var MODEL_URL = 'http://hifi-production.s3.amazonaws.com/tutorials/pingPongGun/Pingpong-Gun-New.fbx';
|
||||
var COLLISION_HULL_URL = 'http://hifi-production.s3.amazonaws.com/tutorials/pingPongGun/Pingpong-Gun-New.obj';
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
|
||||
var pingPongGunProperties = {
|
||||
type: "Model",
|
||||
name: "Tutorial Ping Pong Gun",
|
||||
modelURL: MODEL_URL,
|
||||
shapeType: 'compound',
|
||||
compoundShapeURL: COLLISION_HULL_URL,
|
||||
script: SCRIPT_URL,
|
||||
position: center,
|
||||
dimensions: {
|
||||
x: 0.125,
|
||||
y: 0.3875,
|
||||
z: 0.9931
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -5.0,
|
||||
z: 0
|
||||
},
|
||||
lifetime: 3600,
|
||||
dynamic: true,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
},
|
||||
wearable: {
|
||||
joints: {
|
||||
RightHand: [{
|
||||
x: 0.1177130937576294,
|
||||
y: 0.12922893464565277,
|
||||
z: 0.08307232707738876
|
||||
}, {
|
||||
x: 0.4934672713279724,
|
||||
y: 0.3605862259864807,
|
||||
z: 0.6394805908203125,
|
||||
w: -0.4664038419723511
|
||||
}],
|
||||
LeftHand: [{
|
||||
x: 0.09151676297187805,
|
||||
y: 0.13639454543590546,
|
||||
z: 0.09354984760284424
|
||||
}, {
|
||||
x: -0.19628101587295532,
|
||||
y: 0.6418180465698242,
|
||||
z: 0.2830369472503662,
|
||||
w: 0.6851521730422974
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var pingPongGun = Entities.addEntity(pingPongGunProperties);
|
||||
|
||||
Script.stop();
|
36
scripts/tutorials/createSoundMaker.js
Normal file
36
scripts/tutorials/createSoundMaker.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var SCRIPT_URL = "http://hifi-production.s3.amazonaws.com/tutorials/entity_scripts/soundMaker.js";
|
||||
var MODEL_URL = "http://hifi-production.s3.amazonaws.com/tutorials/soundMaker/Front-Desk-Bell.fbx";
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(1, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
function makeBell() {
|
||||
var soundMakerProperties = {
|
||||
position: center,
|
||||
type: 'Model',
|
||||
modelURL: MODEL_URL,
|
||||
script: SCRIPT_URL,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -5.0,
|
||||
z: 0
|
||||
},
|
||||
lifetime: 3600
|
||||
}
|
||||
|
||||
var soundMaker = Entities.addEntity(soundMakerProperties);
|
||||
Script.stop();
|
||||
}
|
||||
|
||||
makeBell();
|
268
scripts/tutorials/entity_scripts/flashlight.js
Normal file
268
scripts/tutorials/entity_scripts/flashlight.js
Normal file
|
@ -0,0 +1,268 @@
|
|||
//
|
||||
// flashlight.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
//
|
||||
// Created by Sam Gateau on 9/9/15.
|
||||
// Additions by James B. Pollack @imgntn on 9/21/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is a toy script that can be added to the Flashlight model entity:
|
||||
// "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"
|
||||
// that creates a spotlight attached with the flashlight model while the entity is grabbed
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
|
||||
var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav';
|
||||
var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav';
|
||||
|
||||
//we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them
|
||||
//if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value.
|
||||
var LIFETIME = 100;
|
||||
|
||||
|
||||
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
||||
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||
function Flashlight() {
|
||||
return;
|
||||
}
|
||||
|
||||
//if the trigger value goes below this while held, the flashlight will turn off. if it goes above, it will
|
||||
var DISABLE_LIGHT_THRESHOLD = 0.7;
|
||||
|
||||
// These constants define the Spotlight position and orientation relative to the model
|
||||
var MODEL_LIGHT_POSITION = {
|
||||
x: 0,
|
||||
y: -0.3,
|
||||
z: 0
|
||||
};
|
||||
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
|
||||
x: 1,
|
||||
y: 0,
|
||||
z: 0
|
||||
});
|
||||
|
||||
//there's another small point light we put at the end of the flashlight to make the end glow
|
||||
|
||||
var GLOW_LIGHT_POSITION = {
|
||||
x: 0,
|
||||
y: -0.1,
|
||||
z: 0
|
||||
};
|
||||
|
||||
// Evaluate the world light entity positions and orientations from the model ones
|
||||
function evalLightWorldTransform(modelPosition, modelRotation) {
|
||||
|
||||
return {
|
||||
p: Vec3.sum(modelPosition, Vec3.multiplyQbyV(modelRotation, MODEL_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRotation, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
}
|
||||
|
||||
function glowLightWorldTransform(modelPosition, modelRotation) {
|
||||
return {
|
||||
p: Vec3.sum(modelPosition, Vec3.multiplyQbyV(modelRotation, GLOW_LIGHT_POSITION)),
|
||||
q: Quat.multiply(modelRotation, MODEL_LIGHT_ROTATION)
|
||||
};
|
||||
}
|
||||
|
||||
Flashlight.prototype = {
|
||||
lightOn: false,
|
||||
hand: null,
|
||||
whichHand: null,
|
||||
hasSpotlight: false,
|
||||
spotlight: null,
|
||||
|
||||
startNearGrab: function(entityID, args) {
|
||||
//this first second parameter in this method contains arguments from the grab script, the first of which is the 'hand' that was used to start the grab
|
||||
this.hand = args[0];
|
||||
if (!this.hasSpotlight) {
|
||||
|
||||
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||
|
||||
|
||||
//this light casts the beam
|
||||
this.spotlight = Entities.addEntity({
|
||||
type: "Light",
|
||||
isSpotlight: true,
|
||||
dimensions: {
|
||||
x: 2,
|
||||
y: 2,
|
||||
z: 20
|
||||
},
|
||||
parentID: this.entityID,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
intensity: 1,
|
||||
falloffRadius:0.9,
|
||||
exponent: 0.5,
|
||||
cutoff: 20,
|
||||
lifetime: LIFETIME,
|
||||
position: lightTransform.p,
|
||||
rotation: lightTransform.q,
|
||||
});
|
||||
|
||||
//this light creates the effect of a bulb at the end of the flashlight
|
||||
this.glowLight = Entities.addEntity({
|
||||
type: "Light",
|
||||
dimensions: {
|
||||
x: 0.25,
|
||||
y: 0.25,
|
||||
z: 0.25
|
||||
},
|
||||
parentID: this.entityID,
|
||||
isSpotlight: false,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
exponent: 0,
|
||||
intensity:1.0,
|
||||
falloffRadius:0.3,
|
||||
lifetime: LIFETIME,
|
||||
cutoff: 90, // in degrees
|
||||
position: glowLightTransform.p,
|
||||
rotation: glowLightTransform.q,
|
||||
|
||||
});
|
||||
|
||||
this.hasSpotlight = true;
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
startEquip: function(id, params) {
|
||||
this.startNearGrab(id, params);
|
||||
},
|
||||
|
||||
setWhichHand: function() {
|
||||
this.whichHand = this.hand;
|
||||
},
|
||||
|
||||
continueNearGrab: function() {
|
||||
if (this.whichHand === null) {
|
||||
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
|
||||
this.setWhichHand();
|
||||
} else {
|
||||
this.changeLightWithTriggerPressure(this.whichHand);
|
||||
}
|
||||
},
|
||||
continueEquip: function(entityID, args) {
|
||||
this.continueNearGrab(entityID, args);
|
||||
},
|
||||
|
||||
releaseGrab: function(entityID, args) {
|
||||
//delete the lights and reset state
|
||||
if (this.hasSpotlight) {
|
||||
Entities.deleteEntity(this.spotlight);
|
||||
Entities.deleteEntity(this.glowLight);
|
||||
this.hasSpotlight = false;
|
||||
this.glowLight = null;
|
||||
this.spotlight = null;
|
||||
this.whichHand = null;
|
||||
this.lightOn = false;
|
||||
}
|
||||
},
|
||||
|
||||
releaseEquip: function(entityID, args) {
|
||||
this.releaseGrab(entityID, args);
|
||||
},
|
||||
|
||||
changeLightWithTriggerPressure: function(flashLightHand) {
|
||||
//read the value of the controller trigger and use it to turn the light on / off when the value reaches certain thresholds
|
||||
if (flashLightHand === 'LEFT') {
|
||||
this.triggerValue = Controller.getValue(Controller.Standard.LT);
|
||||
}
|
||||
if (flashLightHand === 'RIGHT') {
|
||||
this.triggerValue = Controller.getValue(Controller.Standard.RT);
|
||||
|
||||
}
|
||||
|
||||
if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) {
|
||||
this.turnLightOff();
|
||||
} else if (this.triggerValue >= DISABLE_LIGHT_THRESHOLD && this.lightOn === false) {
|
||||
this.turnLightOn();
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
turnLightOff: function() {
|
||||
this.playSoundAtCurrentPosition(false);
|
||||
Entities.editEntity(this.spotlight, {
|
||||
intensity: 0
|
||||
});
|
||||
Entities.editEntity(this.glowLight, {
|
||||
intensity: 0
|
||||
});
|
||||
this.lightOn = false;
|
||||
},
|
||||
|
||||
turnLightOn: function() {
|
||||
this.playSoundAtCurrentPosition(true);
|
||||
|
||||
Entities.editEntity(this.glowLight, {
|
||||
intensity: 2
|
||||
});
|
||||
Entities.editEntity(this.spotlight, {
|
||||
intensity: 2
|
||||
});
|
||||
this.lightOn = true;
|
||||
},
|
||||
|
||||
playSoundAtCurrentPosition: function(playOnSound) {
|
||||
//when someone grabs, play a clicking noise for turning the flashlight on/off
|
||||
var position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||
|
||||
var audioProperties = {
|
||||
volume: 0.25,
|
||||
position: position
|
||||
};
|
||||
|
||||
if (playOnSound) {
|
||||
Audio.playSound(this.ON_SOUND, audioProperties);
|
||||
} else {
|
||||
Audio.playSound(this.OFF_SOUND, audioProperties);
|
||||
}
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
// preload() will be called when the entity has become visible (or known) to the interface
|
||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||
// * preloading sounds
|
||||
this.entityID = entityID;
|
||||
this.ON_SOUND = SoundCache.getSound(ON_SOUND_URL);
|
||||
this.OFF_SOUND = SoundCache.getSound(OFF_SOUND_URL);
|
||||
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||
// or because we've left the domain or quit the application.
|
||||
if (this.hasSpotlight) {
|
||||
Entities.deleteEntity(this.spotlight);
|
||||
Entities.deleteEntity(this.glowLight);
|
||||
this.hasSpotlight = false;
|
||||
this.glowLight = null;
|
||||
this.spotlight = null;
|
||||
this.whichHand = null;
|
||||
this.lightOn = false;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// entity should return a newly constructed object of our type
|
||||
return new Flashlight();
|
||||
});
|
150
scripts/tutorials/entity_scripts/golfClub.js
Normal file
150
scripts/tutorials/entity_scripts/golfClub.js
Normal file
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
// golfClub.js
|
||||
//
|
||||
// Created by Philip Rosedale on April 11, 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// A simple golf club. If you have equipped it, and pull trigger, it will either make
|
||||
// you a new golf ball, or take you to your ball if one is not made.
|
||||
|
||||
(function() {
|
||||
var ball = null;
|
||||
var collisionSoundURL = "http://hifi-production.s3.amazonaws.com/tutorials/golfClub/collision1.wav";
|
||||
var triggerState = false;
|
||||
var BALL_GRAVITY = -9.8;
|
||||
var BALL_START_VELOCITY = 0.1;
|
||||
var BALL_MAX_RANGE = 10;
|
||||
var BALL_DROP_DISTANCE = 0.6;
|
||||
var BALL_DIAMETER = 0.07;
|
||||
var BALL_LIFETIME = 3600;
|
||||
var MAX_BRAKING_SPEED = 0.2;
|
||||
var BALL_BRAKING_RATE = 0.5;
|
||||
|
||||
var TRIGGER_CONTROLS = [
|
||||
Controller.Standard.LT,
|
||||
Controller.Standard.RT,
|
||||
];
|
||||
|
||||
function triggerPulled(hand) {
|
||||
// Return true if the trigger has just been pulled
|
||||
var triggerValue = Controller.getValue(TRIGGER_CONTROLS[hand]);
|
||||
var oldTriggerState = triggerState;
|
||||
var TRIGGER_PULL_THRESHOLD = 0.5;
|
||||
var TRIGGER_RELEASE_THRESHOLD = 0.4;
|
||||
if (triggerValue > TRIGGER_PULL_THRESHOLD) {
|
||||
triggerState = true;
|
||||
} else if (triggerValue < TRIGGER_RELEASE_THRESHOLD) {
|
||||
triggerState = false;
|
||||
}
|
||||
return (triggerState && (oldTriggerState != triggerState));
|
||||
}
|
||||
|
||||
function ballPosition(ball) {
|
||||
// return the position of this entity
|
||||
var properties = Entities.getEntityProperties(ball, ['position']);
|
||||
if (!properties) {
|
||||
return null;
|
||||
} else {
|
||||
return properties.position;
|
||||
}
|
||||
}
|
||||
|
||||
function gotoPointOnGround(position, away) {
|
||||
// Position yourself facing in the direction you were originally facing, but with a
|
||||
// point on the ground *away* meters from *position* and in front of you.
|
||||
|
||||
var offset = Quat.getFront(MyAvatar.orientation);
|
||||
offset.y = 0.0;
|
||||
offset = Vec3.multiply(-away, Vec3.normalize(offset));
|
||||
var newAvatarPosition = Vec3.sum(position, offset);
|
||||
|
||||
// Assuming position is on ground, put me distance from eyes to hips higher, plus a 50% adjust for longer legs.
|
||||
// TODO: Need avatar callback for exact foot-to-hips height.
|
||||
|
||||
var halfHeight = avatarHalfHeight();
|
||||
print("Height = " + halfHeight);
|
||||
newAvatarPosition.y += (halfHeight * 1.5);
|
||||
MyAvatar.position = newAvatarPosition;
|
||||
}
|
||||
|
||||
function inFrontOfMe() {
|
||||
return Vec3.sum(MyAvatar.position, Vec3.multiply(BALL_DROP_DISTANCE, Quat.getFront(MyAvatar.orientation)));
|
||||
}
|
||||
|
||||
function avatarHalfHeight() {
|
||||
return MyAvatar.getDefaultEyePosition().y - MyAvatar.position.y;
|
||||
}
|
||||
|
||||
function brakeBall(ball) {
|
||||
// Check the ball's velocity and slow it down if beyond a threshold
|
||||
var properties = Entities.getEntityProperties(ball, ['velocity']);
|
||||
if (properties) {
|
||||
var velocity = Vec3.length(properties.velocity);
|
||||
if ((velocity > 0) && (velocity < MAX_BRAKING_SPEED)) {
|
||||
Entities.editEntity(ball, {
|
||||
velocity: Vec3.multiply(BALL_BRAKING_RATE, properties.velocity)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeBall(position) {
|
||||
// Create a new sphere entity
|
||||
ball = Entities.addEntity({
|
||||
type: "Sphere",
|
||||
position: position,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
dimensions: {
|
||||
x: BALL_DIAMETER,
|
||||
y: BALL_DIAMETER,
|
||||
z: BALL_DIAMETER
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: BALL_GRAVITY,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: BALL_START_VELOCITY,
|
||||
z: 0
|
||||
},
|
||||
friction: 0.5,
|
||||
restitution: 0.5,
|
||||
shapeType: "sphere",
|
||||
dynamic: true,
|
||||
lifetime: BALL_LIFETIME,
|
||||
collisionSoundURL: collisionSoundURL
|
||||
});
|
||||
}
|
||||
|
||||
function checkClub(params) {
|
||||
var hand = params[0] == "left" ? 0 : 1;
|
||||
var makeNewBall = false;
|
||||
if (triggerPulled(hand)) {
|
||||
// If trigger just pulled, either drop new ball or go to existing one
|
||||
var position = ballPosition(ball);
|
||||
if (position && (Vec3.distance(MyAvatar.position, position) < BALL_MAX_RANGE)) {
|
||||
gotoPointOnGround(position, BALL_DROP_DISTANCE);
|
||||
} else {
|
||||
Entities.deleteEntity(ball);
|
||||
makeBall(inFrontOfMe());
|
||||
}
|
||||
}
|
||||
brakeBall(ball);
|
||||
}
|
||||
|
||||
this.continueEquip = function(id, params) {
|
||||
// While holding the club, continuously check for trigger pull and brake ball if moving.
|
||||
checkClub(params);
|
||||
}
|
||||
|
||||
});
|
177
scripts/tutorials/entity_scripts/pingPongGun.js
Normal file
177
scripts/tutorials/entity_scripts/pingPongGun.js
Normal file
|
@ -0,0 +1,177 @@
|
|||
// pingPongGun.js
|
||||
//
|
||||
// Script Type: Entity
|
||||
// Created by James B. Pollack @imgntn on 9/21/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script shoots a ping pong ball.
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
var _this = this;
|
||||
|
||||
var SHOOTING_SOUND_URL = 'http://hifi-production.s3.amazonaws.com/tutorials/pingPongGun/pong_sound.wav';
|
||||
|
||||
function PingPongGun() {
|
||||
return;
|
||||
}
|
||||
|
||||
//if the trigger value goes below this value, reload the gun.
|
||||
var RELOAD_THRESHOLD = 0.95;
|
||||
var GUN_TIP_FWD_OFFSET = -0.6;
|
||||
var GUN_TIP_UP_OFFSET = 0.12;
|
||||
var GUN_FORCE = 9;
|
||||
var BALL_RESTITUTION = 0.6;
|
||||
var BALL_LINEAR_DAMPING = 0.4;
|
||||
var BALL_GRAVITY = {
|
||||
x: 0,
|
||||
y: -4.8,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var PING_PONG_GUN_GRAVITY = {
|
||||
x: 0,
|
||||
y: -10,
|
||||
z: 0
|
||||
};
|
||||
|
||||
var BALL_DIMENSIONS = {
|
||||
x: 0.04,
|
||||
y: 0.04,
|
||||
z: 0.04
|
||||
};
|
||||
|
||||
|
||||
var BALL_COLOR = {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
};
|
||||
|
||||
var TRIGGER_CONTROLS = [
|
||||
Controller.Standard.LT,
|
||||
Controller.Standard.RT,
|
||||
];
|
||||
|
||||
|
||||
PingPongGun.prototype = {
|
||||
hand: null,
|
||||
gunTipPosition: null,
|
||||
canShoot: false,
|
||||
canShootTimeout: null,
|
||||
|
||||
startEquip: function(entityID, args) {
|
||||
this.hand = args[0] == "left" ? 0 : 1;
|
||||
},
|
||||
|
||||
continueEquip: function(entityID, args) {
|
||||
if (this.canShootTimeout !== null) {
|
||||
Script.clearTimeout(this.canShootTimeout);
|
||||
}
|
||||
this.checkTriggerPressure(this.hand);
|
||||
},
|
||||
|
||||
releaseEquip: function(entityID, args) {
|
||||
var _this = this;
|
||||
this.canShootTimeout = Script.setTimeout(function() {
|
||||
_this.canShoot = false;
|
||||
}, 250);
|
||||
},
|
||||
|
||||
checkTriggerPressure: function(gunHand) {
|
||||
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[gunHand]);
|
||||
if (this.triggerValue < RELOAD_THRESHOLD) {
|
||||
this.canShoot = true;
|
||||
} else if (this.triggerValue >= RELOAD_THRESHOLD && this.canShoot === true) {
|
||||
var gunProperties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
|
||||
this.shootBall(gunProperties);
|
||||
this.canShoot = false;
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
shootBall: function(gunProperties) {
|
||||
var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, 180, 0)));
|
||||
forwardVec = Vec3.normalize(forwardVec);
|
||||
forwardVec = Vec3.multiply(forwardVec, GUN_FORCE);
|
||||
|
||||
var properties = {
|
||||
name: 'Tutorial Ping Pong Ball',
|
||||
shapeType: 'sphere',
|
||||
type: 'Sphere',
|
||||
color: BALL_COLOR,
|
||||
dimensions: BALL_DIMENSIONS,
|
||||
damping: BALL_LINEAR_DAMPING,
|
||||
gravity: BALL_GRAVITY,
|
||||
restitution: BALL_RESTITUTION,
|
||||
dynamic: true,
|
||||
rotation: gunProperties.rotation,
|
||||
position: this.getGunTipPosition(gunProperties),
|
||||
gravity: PING_PONG_GUN_GRAVITY,
|
||||
velocity: forwardVec,
|
||||
lifetime: 10
|
||||
};
|
||||
|
||||
Entities.addEntity(properties);
|
||||
|
||||
this.playSoundAtCurrentPosition(gunProperties.position);
|
||||
},
|
||||
|
||||
playSoundAtCurrentPosition: function(position) {
|
||||
var audioProperties = {
|
||||
volume: 0.2,
|
||||
position: position
|
||||
};
|
||||
|
||||
Audio.playSound(this.SHOOTING_SOUND, audioProperties);
|
||||
},
|
||||
|
||||
getGunTipPosition: function(properties) {
|
||||
//the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position
|
||||
var frontVector = Quat.getFront(properties.rotation);
|
||||
var frontOffset = Vec3.multiply(frontVector, GUN_TIP_FWD_OFFSET);
|
||||
var upVector = Quat.getUp(properties.rotation);
|
||||
var upOffset = Vec3.multiply(upVector, GUN_TIP_UP_OFFSET);
|
||||
|
||||
var gunTipPosition = Vec3.sum(properties.position, frontOffset);
|
||||
gunTipPosition = Vec3.sum(gunTipPosition, upOffset);
|
||||
|
||||
return gunTipPosition;
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.SHOOTING_SOUND = SoundCache.getSound(SHOOTING_SOUND_URL);
|
||||
// this.createTipEntity(entityID);
|
||||
},
|
||||
createTipEntity: function(entityID) {
|
||||
//for debugging where its going to shoot from
|
||||
var gunProperties = Entities.getEntityProperties(entityID, ["position", "rotation"]);
|
||||
|
||||
var tipProps = {
|
||||
name: 'Ping pong tip test',
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
z: 0.1
|
||||
},
|
||||
color: {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0
|
||||
},
|
||||
type: 'Box',
|
||||
parentID: entityID,
|
||||
position: this.getGunTipPosition(gunProperties)
|
||||
};
|
||||
var tip = Entities.addEntity(tipProps);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// entity scripts should return a newly constructed object of our type
|
||||
return new PingPongGun();
|
||||
});
|
28
scripts/tutorials/entity_scripts/soundMaker.js
Normal file
28
scripts/tutorials/entity_scripts/soundMaker.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function(){
|
||||
|
||||
var soundURL ='http://hifi-production.s3.amazonaws.com/tutorials/soundMaker/bell.wav';
|
||||
var ringSound;
|
||||
|
||||
this.preload = function(entityID) {
|
||||
print("preload("+entityID+")");
|
||||
ringSound = SoundCache.getSound(soundURL);
|
||||
};
|
||||
|
||||
this.clickDownOnEntity = function(entityID, mouseEvent) {
|
||||
var bellPosition = Entities.getEntityProperties(entityID).position;
|
||||
print("clickDownOnEntity()...");
|
||||
Audio.playSound(ringSound, {
|
||||
position: bellPosition,
|
||||
volume: 0.5
|
||||
});
|
||||
};
|
||||
|
||||
})
|
112
scripts/tutorials/giveAvatarMagicFingers.js
Normal file
112
scripts/tutorials/giveAvatarMagicFingers.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
//
|
||||
// Created by Chris Collins on April 11, 2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Attaches particles to your finger joints. Lots of fun!
|
||||
|
||||
|
||||
|
||||
var handiness = 'both'; // left, right or both
|
||||
var particleFingers = ['HandPinky4', 'HandRing4', 'HandIndex4', 'HandThumb4', 'HandMiddle4'];
|
||||
|
||||
var particleEntities = [];
|
||||
var PARICLE_NAME_BASE = 'spawnedFingerParticle'
|
||||
|
||||
// what the actual particles look like
|
||||
var particleProperties = {
|
||||
type: 'ParticleEffect',
|
||||
parentID: MyAvatar.sessionUUID,
|
||||
color: {
|
||||
red: 125,
|
||||
green: 125,
|
||||
blue: 125
|
||||
},
|
||||
isEmitting: 1,
|
||||
maxParticles: 1000,
|
||||
lifespan: 1,
|
||||
emitRate: 100,
|
||||
emitSpeed: 0,
|
||||
speedSpread: 0,
|
||||
emitOrientation: {
|
||||
x: -0.7035577893257141,
|
||||
y: -0.000015259007341228426,
|
||||
z: -0.000015259007341228426,
|
||||
w: 0.7106381058692932
|
||||
},
|
||||
emitRadiusStart: 1,
|
||||
polarStart: 0,
|
||||
polarFinish: 0,
|
||||
azimuthFinish: 3.1415927410125732,
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
particleRadius: 0.004999999888241291,
|
||||
radiusSpread: 0,
|
||||
radiusStart: 0.0010000000474974513,
|
||||
radiusFinish: 0.0010000000474974513,
|
||||
colorSpread: {
|
||||
red: 125,
|
||||
green: 125,
|
||||
blue: 125
|
||||
},
|
||||
colorStart: {
|
||||
red: 125,
|
||||
green: 125,
|
||||
blue: 125
|
||||
},
|
||||
colorFinish: {
|
||||
red: 125,
|
||||
green: 125,
|
||||
blue: 125
|
||||
},
|
||||
alpha: 1,
|
||||
alphaSpread: 0,
|
||||
alphaStart: 1,
|
||||
alphaFinish: 0,
|
||||
emitterShouldTrail: true,
|
||||
textures: 'http://hifi-production.s3.amazonaws.com/tutorials/particleFingers/smoke.png',
|
||||
lifetime: 3600
|
||||
};
|
||||
|
||||
function createParticleAtFinger(jointName) {
|
||||
var jointID = MyAvatar.jointNames.indexOf(jointName);
|
||||
particleProperties.name = PARICLE_NAME_BASE + jointName;
|
||||
particleProperties.parentJointIndex = jointID;
|
||||
position = MyAvatar.getJointPosition(jointName);
|
||||
return Entities.addEntity(particleProperties);
|
||||
}
|
||||
|
||||
function addParticlesForHand(handPrefix) {
|
||||
for (var i = 0; i < particleFingers.length; i++) {
|
||||
particleEntities.push(createParticleAtFinger(handPrefix + particleFingers[i]));
|
||||
print(handPrefix + particleFingers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
for (var i = 0; i < particleEntities.length; i++) {
|
||||
// Fixes a crash on shutdown:
|
||||
// Entities.editEntity(particleEntities[i], { parentID: '' });
|
||||
Entities.deleteEntity(particleEntities[i]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (handiness === "both" || handiness === "left") {
|
||||
addParticlesForHand("Left");
|
||||
}
|
||||
if (handiness === "both" || handiness === "right") {
|
||||
addParticlesForHand("Right");
|
||||
}
|
Loading…
Reference in a new issue