mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-18 11:48:46 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into 20724
This commit is contained in:
commit
4c2c37acce
45 changed files with 3149 additions and 524 deletions
|
@ -18,17 +18,13 @@
|
||||||
|
|
||||||
class EntityNodeData : public OctreeQueryNode {
|
class EntityNodeData : public OctreeQueryNode {
|
||||||
public:
|
public:
|
||||||
EntityNodeData() :
|
|
||||||
OctreeQueryNode(),
|
|
||||||
_lastDeletedEntitiesSentAt(0) { }
|
|
||||||
|
|
||||||
virtual PacketType getMyPacketType() const { return PacketType::EntityData; }
|
virtual PacketType getMyPacketType() const { return PacketType::EntityData; }
|
||||||
|
|
||||||
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
|
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
|
||||||
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
|
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
quint64 _lastDeletedEntitiesSentAt;
|
quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityNodeData_h
|
#endif // hifi_EntityNodeData_h
|
||||||
|
|
|
@ -82,9 +82,15 @@ bool EntityServer::hasSpecialPacketsToSend(const SharedNodePointer& node) {
|
||||||
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
|
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
|
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
|
||||||
|
|
||||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||||
shouldSendDeletedEntities = tree->hasEntitiesDeletedSince(deletedEntitiesSentAt);
|
shouldSendDeletedEntities = tree->hasEntitiesDeletedSince(deletedEntitiesSentAt);
|
||||||
|
|
||||||
|
#ifdef EXTRA_ERASE_DEBUGGING
|
||||||
|
if (shouldSendDeletedEntities) {
|
||||||
|
int elapsed = usecTimestampNow() - deletedEntitiesSentAt;
|
||||||
|
qDebug() << "shouldSendDeletedEntities to node:" << node->getUUID() << "deletedEntitiesSentAt:" << deletedEntitiesSentAt << "elapsed:" << elapsed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return shouldSendDeletedEntities;
|
return shouldSendDeletedEntities;
|
||||||
|
@ -97,7 +103,6 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
|
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
|
||||||
quint64 deletePacketSentAt = usecTimestampNow();
|
quint64 deletePacketSentAt = usecTimestampNow();
|
||||||
|
|
||||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||||
bool hasMoreToSend = true;
|
bool hasMoreToSend = true;
|
||||||
|
|
||||||
|
@ -118,6 +123,13 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN
|
||||||
nodeData->setLastDeletedEntitiesSentAt(deletePacketSentAt);
|
nodeData->setLastDeletedEntitiesSentAt(deletePacketSentAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef EXTRA_ERASE_DEBUGGING
|
||||||
|
if (packetsSent > 0) {
|
||||||
|
qDebug() << "EntityServer::sendSpecialPackets() sent " << packetsSent << "special packets of "
|
||||||
|
<< totalBytes << " total bytes to node:" << node->getUUID();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO: caller is expecting a packetLength, what if we send more than one packet??
|
// TODO: caller is expecting a packetLength, what if we send more than one packet??
|
||||||
return totalBytes;
|
return totalBytes;
|
||||||
}
|
}
|
||||||
|
@ -127,7 +139,6 @@ void EntityServer::pruneDeletedEntities() {
|
||||||
if (tree->hasAnyDeletedEntities()) {
|
if (tree->hasAnyDeletedEntities()) {
|
||||||
|
|
||||||
quint64 earliestLastDeletedEntitiesSent = usecTimestampNow() + 1; // in the future
|
quint64 earliestLastDeletedEntitiesSent = usecTimestampNow() + 1; // in the future
|
||||||
|
|
||||||
DependencyManager::get<NodeList>()->eachNode([&earliestLastDeletedEntitiesSent](const SharedNodePointer& node) {
|
DependencyManager::get<NodeList>()->eachNode([&earliestLastDeletedEntitiesSent](const SharedNodePointer& node) {
|
||||||
if (node->getLinkedData()) {
|
if (node->getLinkedData()) {
|
||||||
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
|
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
|
||||||
|
@ -137,7 +148,6 @@ void EntityServer::pruneDeletedEntities() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
tree->forgetEntitiesDeletedBefore(earliestLastDeletedEntitiesSent);
|
tree->forgetEntitiesDeletedBefore(earliestLastDeletedEntitiesSent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -570,7 +570,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (somethingToSend) {
|
if (somethingToSend && _myServer->wantsVerboseDebug()) {
|
||||||
qDebug() << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval
|
qDebug() << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval
|
||||||
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
|
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
|
||||||
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
|
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
|
||||||
|
|
|
@ -17,8 +17,14 @@
|
||||||
#include <JurisdictionSender.h>
|
#include <JurisdictionSender.h>
|
||||||
|
|
||||||
const int MAX_FILENAME_LENGTH = 1024;
|
const int MAX_FILENAME_LENGTH = 1024;
|
||||||
const int INTERVALS_PER_SECOND = 60;
|
|
||||||
|
/// This is the frequency (hz) that we check the octree server for changes to determine if we need to
|
||||||
|
/// send new "scene" information to the viewers. This will directly effect how quickly edits are
|
||||||
|
/// sent down do viewers. By setting it to 90hz we allow edits happening at 90hz to be sent down
|
||||||
|
/// to viewers at a rate more closely matching the edit rate. It would probably be better to allow
|
||||||
|
/// clients to ask the server to send at a rate consistent with their current vsynch since clients
|
||||||
|
/// can't render any faster than their vsynch even if the server sent them more acurate information
|
||||||
|
const int INTERVALS_PER_SECOND = 90;
|
||||||
const int OCTREE_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND;
|
const int OCTREE_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND;
|
||||||
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for sending octree elements
|
|
||||||
|
|
||||||
#endif // hifi_OctreeServerConsts_h
|
#endif // hifi_OctreeServerConsts_h
|
||||||
|
|
|
@ -17,17 +17,21 @@ var NAMES = new Array("Craig", "Clement", "Jeff"); // ACs names ordered by IDs (
|
||||||
|
|
||||||
// Those variables MUST be common to every scripts
|
// Those variables MUST be common to every scripts
|
||||||
var controlEntitySize = 0.25;
|
var controlEntitySize = 0.25;
|
||||||
var controlEntityPosition = { x: 2000 , y: 0, z: 0 };
|
var controlEntityPosition = { x: 0, y: 0, z: 0 };
|
||||||
|
|
||||||
// Script. DO NOT MODIFY BEYOND THIS LINE.
|
// Script. DO NOT MODIFY BEYOND THIS LINE.
|
||||||
Script.include("../libraries/toolBars.js");
|
Script.include("../libraries/toolBars.js");
|
||||||
|
|
||||||
|
var clip_url = null;
|
||||||
|
var input_text = null;
|
||||||
|
|
||||||
var DO_NOTHING = 0;
|
var DO_NOTHING = 0;
|
||||||
var PLAY = 1;
|
var PLAY = 1;
|
||||||
var PLAY_LOOP = 2;
|
var PLAY_LOOP = 2;
|
||||||
var STOP = 3;
|
var STOP = 3;
|
||||||
var SHOW = 4;
|
var SHOW = 4;
|
||||||
var HIDE = 5;
|
var HIDE = 5;
|
||||||
|
var LOAD = 6;
|
||||||
|
|
||||||
var COLORS = [];
|
var COLORS = [];
|
||||||
COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 };
|
COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 };
|
||||||
|
@ -35,6 +39,7 @@ COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 };
|
||||||
COLORS[STOP] = { red: STOP, green: 0, blue: 0 };
|
COLORS[STOP] = { red: STOP, green: 0, blue: 0 };
|
||||||
COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 };
|
COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 };
|
||||||
COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 };
|
COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 };
|
||||||
|
COLORS[LOAD] = { red: LOAD, green: 0, blue: 0 };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,6 +58,7 @@ var onOffIcon = new Array();
|
||||||
var playIcon = new Array();
|
var playIcon = new Array();
|
||||||
var playLoopIcon = new Array();
|
var playLoopIcon = new Array();
|
||||||
var stopIcon = new Array();
|
var stopIcon = new Array();
|
||||||
|
var loadIcon = new Array();
|
||||||
setupToolBars();
|
setupToolBars();
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,6 +111,14 @@ function setupToolBars() {
|
||||||
visible: true
|
visible: true
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
|
loadIcon[i] = toolBars[i].addTool({
|
||||||
|
imageURL: TOOL_ICON_URL + "recording-upload.svg",
|
||||||
|
width: Tool.IMAGE_WIDTH,
|
||||||
|
height: Tool.IMAGE_HEIGHT,
|
||||||
|
alpha: ALPHA_OFF,
|
||||||
|
visible: true
|
||||||
|
}, false);
|
||||||
|
|
||||||
nameOverlays.push(Overlays.addOverlay("text", {
|
nameOverlays.push(Overlays.addOverlay("text", {
|
||||||
backgroundColor: { red: 0, green: 0, blue: 0 },
|
backgroundColor: { red: 0, green: 0, blue: 0 },
|
||||||
font: { size: TEXT_HEIGHT },
|
font: { size: TEXT_HEIGHT },
|
||||||
|
@ -129,11 +143,13 @@ function sendCommand(id, action) {
|
||||||
toolBars[id].setAlpha(ALPHA_ON, playIcon[id]);
|
toolBars[id].setAlpha(ALPHA_ON, playIcon[id]);
|
||||||
toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]);
|
toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]);
|
||||||
toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]);
|
toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]);
|
||||||
|
toolBars[id].setAlpha(ALPHA_ON, loadIcon[id]);
|
||||||
} else if (action === HIDE) {
|
} else if (action === HIDE) {
|
||||||
toolBars[id].selectTool(onOffIcon[id], true);
|
toolBars[id].selectTool(onOffIcon[id], true);
|
||||||
toolBars[id].setAlpha(ALPHA_OFF, playIcon[id]);
|
toolBars[id].setAlpha(ALPHA_OFF, playIcon[id]);
|
||||||
toolBars[id].setAlpha(ALPHA_OFF, playLoopIcon[id]);
|
toolBars[id].setAlpha(ALPHA_OFF, playLoopIcon[id]);
|
||||||
toolBars[id].setAlpha(ALPHA_OFF, stopIcon[id]);
|
toolBars[id].setAlpha(ALPHA_OFF, stopIcon[id]);
|
||||||
|
toolBars[id].setAlpha(ALPHA_OFF, loadIcon[id]);
|
||||||
} else if (toolBars[id].toolSelected(onOffIcon[id])) {
|
} else if (toolBars[id].toolSelected(onOffIcon[id])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -148,6 +164,8 @@ function sendCommand(id, action) {
|
||||||
var position = { x: controlEntityPosition.x + id * controlEntitySize,
|
var position = { x: controlEntityPosition.x + id * controlEntitySize,
|
||||||
y: controlEntityPosition.y, z: controlEntityPosition.z };
|
y: controlEntityPosition.y, z: controlEntityPosition.z };
|
||||||
Entities.addEntity({
|
Entities.addEntity({
|
||||||
|
name: "Actor Controller",
|
||||||
|
userData: clip_url,
|
||||||
type: "Box",
|
type: "Box",
|
||||||
position: position,
|
position: position,
|
||||||
dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize },
|
dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize },
|
||||||
|
@ -173,6 +191,8 @@ function mousePressEvent(event) {
|
||||||
sendCommand(i, PLAY_LOOP);
|
sendCommand(i, PLAY_LOOP);
|
||||||
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
sendCommand(i, STOP);
|
sendCommand(i, STOP);
|
||||||
|
} else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
|
sendCommand(i, LOAD);
|
||||||
} else {
|
} else {
|
||||||
// Check individual controls
|
// Check individual controls
|
||||||
for (i = 0; i < NUM_AC; i++) {
|
for (i = 0; i < NUM_AC; i++) {
|
||||||
|
@ -188,6 +208,12 @@ function mousePressEvent(event) {
|
||||||
sendCommand(i, PLAY_LOOP);
|
sendCommand(i, PLAY_LOOP);
|
||||||
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
sendCommand(i, STOP);
|
sendCommand(i, STOP);
|
||||||
|
} else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
|
input_text = Window.prompt("Insert the url of the clip: ","");
|
||||||
|
if(!(input_text === "" || input_text === null)){
|
||||||
|
clip_url = input_text;
|
||||||
|
sendCommand(i, LOAD);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,9 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
|
||||||
|
|
||||||
// Set the following variables to the values needed
|
// Set the following variables to the values needed
|
||||||
var filename = "/Users/clement/Desktop/recording.hfr";
|
var clip_url = null;
|
||||||
var playFromCurrentLocation = true;
|
var playFromCurrentLocation = true;
|
||||||
var useDisplayName = true;
|
var useDisplayName = true;
|
||||||
var useAttachments = true;
|
var useAttachments = true;
|
||||||
|
@ -21,8 +20,6 @@ var useAvatarModel = true;
|
||||||
// ID of the agent. Two agents can't have the same ID.
|
// ID of the agent. Two agents can't have the same ID.
|
||||||
var id = 0;
|
var id = 0;
|
||||||
|
|
||||||
// Set avatar model URL
|
|
||||||
Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst?1427169998";
|
|
||||||
// Set position/orientation/scale here if playFromCurrentLocation is true
|
// Set position/orientation/scale here if playFromCurrentLocation is true
|
||||||
Avatar.position = { x:1, y: 1, z: 1 };
|
Avatar.position = { x:1, y: 1, z: 1 };
|
||||||
Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||||
|
@ -30,7 +27,7 @@ Avatar.scale = 1.0;
|
||||||
|
|
||||||
// Those variables MUST be common to every scripts
|
// Those variables MUST be common to every scripts
|
||||||
var controlEntitySize = 0.25;
|
var controlEntitySize = 0.25;
|
||||||
var controlEntityPosition = { x: 2000, y: 0, z: 0 };
|
var controlEntityPosition = { x: 0, y: 0, z: 0 };
|
||||||
|
|
||||||
// Script. DO NOT MODIFY BEYOND THIS LINE.
|
// Script. DO NOT MODIFY BEYOND THIS LINE.
|
||||||
var DO_NOTHING = 0;
|
var DO_NOTHING = 0;
|
||||||
|
@ -39,6 +36,7 @@ var PLAY_LOOP = 2;
|
||||||
var STOP = 3;
|
var STOP = 3;
|
||||||
var SHOW = 4;
|
var SHOW = 4;
|
||||||
var HIDE = 5;
|
var HIDE = 5;
|
||||||
|
var LOAD = 6;
|
||||||
|
|
||||||
var COLORS = [];
|
var COLORS = [];
|
||||||
COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 };
|
COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 };
|
||||||
|
@ -46,10 +44,11 @@ COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 };
|
||||||
COLORS[STOP] = { red: STOP, green: 0, blue: 0 };
|
COLORS[STOP] = { red: STOP, green: 0, blue: 0 };
|
||||||
COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 };
|
COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 };
|
||||||
COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 };
|
COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 };
|
||||||
|
COLORS[LOAD] = { red: LOAD, green: 0, blue: 0 };
|
||||||
|
|
||||||
controlEntityPosition.x += id * controlEntitySize;
|
controlEntityPosition.x += id * controlEntitySize;
|
||||||
|
|
||||||
Avatar.loadRecording(filename);
|
Avatar.loadRecording(clip_url);
|
||||||
|
|
||||||
Avatar.setPlayFromCurrentLocation(playFromCurrentLocation);
|
Avatar.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||||
Avatar.setPlayerUseDisplayName(useDisplayName);
|
Avatar.setPlayerUseDisplayName(useDisplayName);
|
||||||
|
@ -69,6 +68,8 @@ function setupEntityViewer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAction(controlEntity) {
|
function getAction(controlEntity) {
|
||||||
|
clip_url = controlEntity.userData;
|
||||||
|
|
||||||
if (controlEntity === null ||
|
if (controlEntity === null ||
|
||||||
controlEntity.position.x !== controlEntityPosition.x ||
|
controlEntity.position.x !== controlEntityPosition.x ||
|
||||||
controlEntity.position.y !== controlEntityPosition.y ||
|
controlEntity.position.y !== controlEntityPosition.y ||
|
||||||
|
@ -141,6 +142,12 @@ function update(event) {
|
||||||
}
|
}
|
||||||
Agent.isAvatar = false;
|
Agent.isAvatar = false;
|
||||||
break;
|
break;
|
||||||
|
case LOAD:
|
||||||
|
print("Load");
|
||||||
|
if(clip_url !== null) {
|
||||||
|
Avatar.loadRecording(clip_url);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case DO_NOTHING:
|
case DO_NOTHING:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -489,7 +489,7 @@ function MyController(hand) {
|
||||||
if (grabbableData.wantsTrigger) {
|
if (grabbableData.wantsTrigger) {
|
||||||
this.setState(STATE_NEAR_TRIGGER);
|
this.setState(STATE_NEAR_TRIGGER);
|
||||||
return;
|
return;
|
||||||
} else if (!props.locked) {
|
} else if (!props.locked && props.collisionsWillMove) {
|
||||||
this.setState(STATE_NEAR_GRABBING);
|
this.setState(STATE_NEAR_GRABBING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
144
examples/drylake/explodeHelicopter.js
Normal file
144
examples/drylake/explodeHelicopter.js
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
var explosionSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/eric/sounds/explosion.wav");
|
||||||
|
|
||||||
|
var partsURLS = [{
|
||||||
|
url: "https://s3.amazonaws.com/hifi-public/eric/models/blade.fbx",
|
||||||
|
dimensions: {
|
||||||
|
x: 2,
|
||||||
|
y: 2,
|
||||||
|
z: 2
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
url: "https://s3.amazonaws.com/hifi-public/eric/models/body.fbx",
|
||||||
|
dimensions: {
|
||||||
|
x: 2.2,
|
||||||
|
y: 2.98,
|
||||||
|
z: 7.96
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
url: "https://s3.amazonaws.com/hifi-public/eric/models/tail.fbx",
|
||||||
|
dimensions: {
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
z: 1
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
var parts = [];
|
||||||
|
var emitters = [];
|
||||||
|
|
||||||
|
var explodePosition;
|
||||||
|
var helicopter;
|
||||||
|
var entities = Entities.findEntities(MyAvatar.position, 2000);
|
||||||
|
for (i = 0; i < entities.length; i++) {
|
||||||
|
var name = Entities.getEntityProperties(entities[i], 'name').name;
|
||||||
|
if (name === "Helicopter") {
|
||||||
|
var helicopter = entities[i];
|
||||||
|
explodeHelicopter(Entities.getEntityProperties(helicopter, 'position').position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function explodeHelicopter(explodePosition) {
|
||||||
|
Audio.playSound(explosionSound, {
|
||||||
|
position: explodePosition,
|
||||||
|
volume: 0.5
|
||||||
|
});
|
||||||
|
Entities.deleteEntity(helicopter);
|
||||||
|
for (var i = 0; i < partsURLS.length; i++) {
|
||||||
|
var position = Vec3.sum(explodePosition, {
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
z: 1
|
||||||
|
});
|
||||||
|
var part = Entities.addEntity({
|
||||||
|
type: "Model",
|
||||||
|
modelURL: partsURLS[i].url,
|
||||||
|
dimensions: partsURLS[i].dimensions,
|
||||||
|
position: position,
|
||||||
|
shapeType: "box",
|
||||||
|
collisionsWillMove: true,
|
||||||
|
damping: 0,
|
||||||
|
gravity: {
|
||||||
|
x: 0,
|
||||||
|
y: -9.6,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
velocity: {
|
||||||
|
x: Math.random(),
|
||||||
|
y: -10,
|
||||||
|
z: Math.random()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var emitter = Entities.addEntity({
|
||||||
|
type: "ParticleEffect",
|
||||||
|
name: "fire",
|
||||||
|
isEmitting: true,
|
||||||
|
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||||
|
position: explodePosition,
|
||||||
|
emitRate: 100,
|
||||||
|
colorStart: {
|
||||||
|
red: 70,
|
||||||
|
green: 70,
|
||||||
|
blue: 137
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
red: 200,
|
||||||
|
green: 99,
|
||||||
|
blue: 42
|
||||||
|
},
|
||||||
|
colorFinish: {
|
||||||
|
red: 255,
|
||||||
|
green: 99,
|
||||||
|
blue: 32
|
||||||
|
},
|
||||||
|
radiusSpread: 0.2,
|
||||||
|
radiusStart: 0.3,
|
||||||
|
radiusEnd: 0.04,
|
||||||
|
particleRadius: 0.09,
|
||||||
|
radiusFinish: 0.0,
|
||||||
|
emitSpeed: 0.1,
|
||||||
|
speedSpread: 0.1,
|
||||||
|
alphaStart: 0.1,
|
||||||
|
alpha: 0.7,
|
||||||
|
alphaFinish: 0.1,
|
||||||
|
emitOrientation: Quat.fromPitchYawRollDegrees(-90, 0, 0),
|
||||||
|
emitDimensions: {
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
z: 0.1
|
||||||
|
},
|
||||||
|
polarFinish: Math.PI,
|
||||||
|
polarStart: 0,
|
||||||
|
|
||||||
|
accelerationSpread: {
|
||||||
|
x: 0.1,
|
||||||
|
y: 0.01,
|
||||||
|
z: 0.1
|
||||||
|
},
|
||||||
|
lifespan: 1,
|
||||||
|
});
|
||||||
|
emitters.push(emitter)
|
||||||
|
parts.push(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.setTimeout(function() {
|
||||||
|
var pos = Entities.getEntityProperties(parts[1], "position").position;
|
||||||
|
Entities.editEntity(emitters[0], {position: Vec3.sum(pos, {x: Math.random(), y: Math.random(), z: Math.random()})});
|
||||||
|
Entities.editEntity(emitters[1], {position: Vec3.sum(pos, {x: Math.random(), y: Math.random(), z: Math.random()})});
|
||||||
|
Entities.editEntity(emitters[2], {position: Vec3.sum(pos, {x: Math.random(), y: Math.random(), z: Math.random()})});
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
parts.forEach(function(part) {
|
||||||
|
Entities.deleteEntity(part);
|
||||||
|
});
|
||||||
|
emitters.forEach(function(emitter){
|
||||||
|
Entities.deleteEntity(emitter);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
132
examples/drylake/helicopter.js
Normal file
132
examples/drylake/helicopter.js
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx?v3";
|
||||||
|
var animationURL = "https://s3.amazonaws.com/hifi-public/eric/models/bladeAnimation.fbx?v7";
|
||||||
|
var spawnPosition = {
|
||||||
|
x: 1031,
|
||||||
|
y: 145,
|
||||||
|
z: 1041
|
||||||
|
};
|
||||||
|
|
||||||
|
var speed = 0;
|
||||||
|
|
||||||
|
var helicopterSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/ryan/helicopter.L.wav");
|
||||||
|
var audioInjector = Audio.playSound(helicopterSound, {
|
||||||
|
volume: 0.3,
|
||||||
|
loop: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// These constants define the Spotlight position and orientation relative to the model
|
||||||
|
var MODEL_LIGHT_POSITION = {
|
||||||
|
x: 2,
|
||||||
|
y: 0,
|
||||||
|
z: -5
|
||||||
|
};
|
||||||
|
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
|
||||||
|
x: 1,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// Evaluate the world light entity positions and orientations from the model ones
|
||||||
|
function evalLightWorldTransform(modelPos, modelRot) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
|
||||||
|
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var helicopter = Entities.addEntity({
|
||||||
|
type: "Model",
|
||||||
|
name: "Helicopter",
|
||||||
|
modelURL: modelURL,
|
||||||
|
animation: {
|
||||||
|
url: animationURL,
|
||||||
|
running: true,
|
||||||
|
fps: 180
|
||||||
|
|
||||||
|
},
|
||||||
|
dimensions: {
|
||||||
|
x: 12.13,
|
||||||
|
y: 3.14,
|
||||||
|
z: 9.92
|
||||||
|
},
|
||||||
|
position: spawnPosition,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var spotlight = Entities.addEntity({
|
||||||
|
type: "Light",
|
||||||
|
name: "helicopter light",
|
||||||
|
intensity: 2,
|
||||||
|
color: {
|
||||||
|
red: 200,
|
||||||
|
green: 200,
|
||||||
|
blue: 255
|
||||||
|
},
|
||||||
|
intensity: 1,
|
||||||
|
dimensions: {
|
||||||
|
x: 2,
|
||||||
|
y: 2,
|
||||||
|
z: 200
|
||||||
|
},
|
||||||
|
exponent: 0.01,
|
||||||
|
cutoff: 10,
|
||||||
|
isSpotlight: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var debugLight = Entities.addEntity({
|
||||||
|
type: "Box",
|
||||||
|
dimensions: {
|
||||||
|
x: .1,
|
||||||
|
y: .1,
|
||||||
|
z: .3
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
red: 200,
|
||||||
|
green: 200,
|
||||||
|
blue: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
Entities.deleteEntity(debugLight);
|
||||||
|
Entities.deleteEntity(helicopter);
|
||||||
|
Entities.deleteEntity(spotlight);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
var modelProperties = Entities.getEntityProperties(helicopter, ['position', 'rotation']);
|
||||||
|
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||||
|
Entities.editEntity(spotlight, {
|
||||||
|
position: lightTransform.p,
|
||||||
|
rotation: lightTransform.q
|
||||||
|
});
|
||||||
|
Entities.editEntity(debugLight, {
|
||||||
|
position: lightTransform.p,
|
||||||
|
rotation: lightTransform.q
|
||||||
|
});
|
||||||
|
|
||||||
|
audioInjector.setOptions({
|
||||||
|
position: modelProperties.position,
|
||||||
|
});
|
||||||
|
|
||||||
|
//Move forward
|
||||||
|
var newRotation = Quat.multiply(modelProperties.rotation, {
|
||||||
|
x: 0,
|
||||||
|
y: .002,
|
||||||
|
z: 0,
|
||||||
|
w: 1
|
||||||
|
})
|
||||||
|
var newPosition = Vec3.sum(modelProperties.position, Vec3.multiply(speed, Quat.getFront(modelProperties.rotation)));
|
||||||
|
Entities.editEntity(helicopter, {
|
||||||
|
position: newPosition,
|
||||||
|
rotation: newRotation
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Script.update.connect(update);
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
73
examples/example/avatarcontrol/handControlledHead.js
Normal file
73
examples/example/avatarcontrol/handControlledHead.js
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
//
|
||||||
|
// handControlledHead.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Alessandro Signa on 10/11/15.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This script allows you to look around, driving the rotation of the avatar's head by the right hand orientation.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
const YAW_MULTIPLIER = 20000;
|
||||||
|
const PITCH_MULTIPLIER = 15000;
|
||||||
|
const EPSILON = 0.001;
|
||||||
|
var firstPress = true;
|
||||||
|
var handPreviousVerticalRotation = 0.0;
|
||||||
|
var handCurrentVerticalRotation = 0.0;
|
||||||
|
var handPreviousHorizontalRotation = 0.0;
|
||||||
|
var handCurrentHorizontalRotation = 0.0;
|
||||||
|
var rotatedHandPosition;
|
||||||
|
var rotatedTipPosition;
|
||||||
|
|
||||||
|
function update(deltaTime) {
|
||||||
|
if(Controller.getValue(Controller.Standard.RightPrimaryThumb)){
|
||||||
|
pitchManager(deltaTime);
|
||||||
|
}else if(!firstPress){
|
||||||
|
firstPress = true;
|
||||||
|
}
|
||||||
|
if(firstPress && MyAvatar.headYaw){
|
||||||
|
MyAvatar.headYaw -= MyAvatar.headYaw/10;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function pitchManager(deltaTime){
|
||||||
|
|
||||||
|
rotatedHandPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, -MyAvatar.bodyYaw, 0), MyAvatar.getRightHandPosition());
|
||||||
|
rotatedTipPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, -MyAvatar.bodyYaw, 0), MyAvatar.getRightHandTipPosition());
|
||||||
|
|
||||||
|
handCurrentVerticalRotation = Vec3.subtract(rotatedTipPosition, rotatedHandPosition).y;
|
||||||
|
handCurrentHorizontalRotation = Vec3.subtract(rotatedTipPosition, rotatedHandPosition).x;
|
||||||
|
|
||||||
|
var handCurrentHorizontalRotationFiltered = handCurrentHorizontalRotation;
|
||||||
|
|
||||||
|
//to avoid yaw drift
|
||||||
|
if((handCurrentHorizontalRotation - handPreviousHorizontalRotation) < EPSILON && (handCurrentHorizontalRotation - handPreviousHorizontalRotation) > -EPSILON){
|
||||||
|
handCurrentHorizontalRotationFiltered = handPreviousHorizontalRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(firstPress){
|
||||||
|
handPreviousVerticalRotation = handCurrentVerticalRotation;
|
||||||
|
handPreviousHorizontalRotation = handCurrentHorizontalRotation;
|
||||||
|
firstPress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyAvatar.headPitch += (handCurrentVerticalRotation - handPreviousVerticalRotation)*PITCH_MULTIPLIER*deltaTime;
|
||||||
|
MyAvatar.headYaw -= (handCurrentHorizontalRotationFiltered - handPreviousHorizontalRotation)*YAW_MULTIPLIER*deltaTime;
|
||||||
|
|
||||||
|
|
||||||
|
handPreviousVerticalRotation = handCurrentVerticalRotation;
|
||||||
|
handPreviousHorizontalRotation = handCurrentHorizontalRotationFiltered;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function clean(){
|
||||||
|
MyAvatar.headYaw = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Script.update.connect(update);
|
||||||
|
Script.scriptEnding.connect(clean);
|
265
examples/example/games/color_busters/colorBusterWand.js
Normal file
265
examples/example/games/color_busters/colorBusterWand.js
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
//
|
||||||
|
// colorBusterWand.js
|
||||||
|
//
|
||||||
|
// Created by James B. Pollack @imgntn on 11/2/2015
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is the entity script that attaches to a wand for the Color Busters game
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
Script.include("../../../libraries/utils.js");
|
||||||
|
|
||||||
|
var COMBINED_COLOR_DURATION = 5;
|
||||||
|
|
||||||
|
var INDICATOR_OFFSET_UP = 0.40;
|
||||||
|
|
||||||
|
var REMOVE_CUBE_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/color_busters/boop.wav';
|
||||||
|
var COMBINE_COLORS_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/color_busters/powerup.wav';
|
||||||
|
|
||||||
|
var COLOR_INDICATOR_DIMENSIONS = {
|
||||||
|
x: 0.10,
|
||||||
|
y: 0.10,
|
||||||
|
z: 0.10
|
||||||
|
};
|
||||||
|
|
||||||
|
var _this;
|
||||||
|
|
||||||
|
function ColorBusterWand() {
|
||||||
|
_this = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorBusterWand.prototype = {
|
||||||
|
combinedColorsTimer: null,
|
||||||
|
soundIsPlaying: false,
|
||||||
|
preload: function(entityID) {
|
||||||
|
print("preload");
|
||||||
|
this.entityID = entityID;
|
||||||
|
this.REMOVE_CUBE_SOUND = SoundCache.getSound(REMOVE_CUBE_SOUND_URL);
|
||||||
|
this.COMBINE_COLORS_SOUND = SoundCache.getSound(COMBINE_COLORS_SOUND_URL);
|
||||||
|
},
|
||||||
|
|
||||||
|
collisionWithEntity: function(me, otherEntity, collision) {
|
||||||
|
var otherProperties = Entities.getEntityProperties(otherEntity, ["name", "userData"]);
|
||||||
|
var myProperties = Entities.getEntityProperties(me, ["userData"]);
|
||||||
|
var myUserData = JSON.parse(myProperties.userData);
|
||||||
|
var otherUserData = JSON.parse(otherProperties.userData);
|
||||||
|
|
||||||
|
if (otherProperties.name === 'Hifi-ColorBusterWand') {
|
||||||
|
print('HIT ANOTHER COLOR WAND!!');
|
||||||
|
if (otherUserData.hifiColorBusterWandKey.colorLocked !== true && myUserData.hifiColorBusterWandKey.colorLocked !== true) {
|
||||||
|
if (otherUserData.hifiColorBusterWandKey.originalColorName === myUserData.hifiColorBusterWandKey.originalColorName) {
|
||||||
|
print('BUT ITS THE SAME COLOR!')
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
print('COMBINE COLORS!' + this.entityID);
|
||||||
|
this.combineColorsWithOtherWand(otherUserData.hifiColorBusterWandKey.originalColorName, myUserData.hifiColorBusterWandKey.originalColorName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherProperties.name === 'Hifi-ColorBusterCube') {
|
||||||
|
if (otherUserData.hifiColorBusterCubeKey.originalColorName === myUserData.hifiColorBusterWandKey.currentColor) {
|
||||||
|
print('HIT THE SAME COLOR CUBE');
|
||||||
|
this.removeCubeOfSameColor(otherEntity);
|
||||||
|
} else {
|
||||||
|
print('HIT A CUBE OF A DIFFERENT COLOR');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
combineColorsWithOtherWand: function(otherColor, myColor) {
|
||||||
|
print('combining my :' + myColor + " with their: " + otherColor);
|
||||||
|
|
||||||
|
if ((myColor === 'violet') || (myColor === 'orange') || (myColor === 'green')) {
|
||||||
|
print('MY WAND ALREADY COMBINED');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newColor;
|
||||||
|
if ((otherColor === 'red' && myColor == 'yellow') || (myColor === 'red' && otherColor === 'yellow')) {
|
||||||
|
//orange
|
||||||
|
newColor = 'orange';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((otherColor === 'red' && myColor == 'blue') || (myColor === 'red' && otherColor === 'blue')) {
|
||||||
|
//violet
|
||||||
|
newColor = 'violet';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((otherColor === 'blue' && myColor == 'yellow') || (myColor === 'blue' && otherColor === 'yellow')) {
|
||||||
|
//green.
|
||||||
|
newColor = 'green';
|
||||||
|
}
|
||||||
|
|
||||||
|
_this.combinedColorsTimer = Script.setTimeout(function() {
|
||||||
|
_this.resetToOriginalColor(myColor);
|
||||||
|
_this.combinedColorsTimer = null;
|
||||||
|
}, COMBINED_COLOR_DURATION * 1000);
|
||||||
|
|
||||||
|
setEntityCustomData('hifiColorBusterWandKey', this.entityID, {
|
||||||
|
owner: MyAvatar.sessionUUID,
|
||||||
|
currentColor: newColor,
|
||||||
|
originalColorName: myColor,
|
||||||
|
colorLocked: false
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.playSoundAtCurrentPosition(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
setCurrentColor: function(newColor) {
|
||||||
|
var color;
|
||||||
|
|
||||||
|
if (newColor === 'orange') {
|
||||||
|
color = {
|
||||||
|
red: 255,
|
||||||
|
green: 165,
|
||||||
|
blue: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newColor === 'violet') {
|
||||||
|
color = {
|
||||||
|
red: 128,
|
||||||
|
green: 0,
|
||||||
|
blue: 128
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newColor === 'green') {
|
||||||
|
color = {
|
||||||
|
red: 0,
|
||||||
|
green: 255,
|
||||||
|
blue: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newColor === 'red') {
|
||||||
|
color = {
|
||||||
|
red: 255,
|
||||||
|
green: 0,
|
||||||
|
blue: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newColor === 'yellow') {
|
||||||
|
color = {
|
||||||
|
red: 255,
|
||||||
|
green: 255,
|
||||||
|
blue: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newColor === 'blue') {
|
||||||
|
color = {
|
||||||
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 255
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Entities.editEntity(this.colorIndicator, {
|
||||||
|
color: color
|
||||||
|
});
|
||||||
|
|
||||||
|
// print('SET THIS COLOR INDICATOR TO:' + newColor);
|
||||||
|
},
|
||||||
|
|
||||||
|
resetToOriginalColor: function(myColor) {
|
||||||
|
setEntityCustomData('hifiColorBusterWandKey', this.entityID, {
|
||||||
|
owner: MyAvatar.sessionUUID,
|
||||||
|
currentColor: myColor,
|
||||||
|
originalColorName: myColor,
|
||||||
|
colorLocked: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setCurrentColor(myColor);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeCubeOfSameColor: function(cube) {
|
||||||
|
this.playSoundAtCurrentPosition(true);
|
||||||
|
Entities.callEntityMethod(cube, 'cubeEnding');
|
||||||
|
Entities.deleteEntity(cube);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
startNearGrab: function() {
|
||||||
|
this.currentProperties = Entities.getEntityProperties(this.entityID);
|
||||||
|
this.createColorIndicator();
|
||||||
|
},
|
||||||
|
|
||||||
|
continueNearGrab: function() {
|
||||||
|
this.currentProperties = Entities.getEntityProperties(this.entityID);
|
||||||
|
|
||||||
|
var color = JSON.parse(this.currentProperties.userData).hifiColorBusterWandKey.currentColor;
|
||||||
|
|
||||||
|
this.setCurrentColor(color);
|
||||||
|
this.updateColorIndicatorLocation();
|
||||||
|
},
|
||||||
|
|
||||||
|
releaseGrab: function() {
|
||||||
|
Entities.deleteEntity(this.colorIndicator);
|
||||||
|
if (this.combinedColorsTimer !== null) {
|
||||||
|
Script.clearTimeout(this.combinedColorsTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
createColorIndicator: function(color) {
|
||||||
|
|
||||||
|
|
||||||
|
var properties = {
|
||||||
|
name: 'Hifi-ColorBusterIndicator',
|
||||||
|
type: 'Box',
|
||||||
|
dimensions: COLOR_INDICATOR_DIMENSIONS,
|
||||||
|
position: this.currentProperties.position,
|
||||||
|
collisionsWillMove: false,
|
||||||
|
ignoreForCollisions: true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.colorIndicator = Entities.addEntity(properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateColorIndicatorLocation: function() {
|
||||||
|
|
||||||
|
var position;
|
||||||
|
|
||||||
|
var upVector = Quat.getUp(this.currentProperties.rotation);
|
||||||
|
var indicatorVector = Vec3.multiply(upVector, INDICATOR_OFFSET_UP);
|
||||||
|
position = Vec3.sum(this.currentProperties.position, indicatorVector);
|
||||||
|
|
||||||
|
var properties = {
|
||||||
|
position: position,
|
||||||
|
rotation: this.currentProperties.rotation
|
||||||
|
}
|
||||||
|
|
||||||
|
Entities.editEntity(this.colorIndicator, properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
playSoundAtCurrentPosition: function(isRemoveCubeSound) {
|
||||||
|
|
||||||
|
var position = Entities.getEntityProperties(this.entityID, "position").position;
|
||||||
|
var audioProperties = {
|
||||||
|
volume: 0.25,
|
||||||
|
position: position
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isRemoveCubeSound === true) {
|
||||||
|
Audio.playSound(this.REMOVE_CUBE_SOUND, audioProperties);
|
||||||
|
} else {
|
||||||
|
Audio.playSound(this.COMBINE_COLORS_SOUND, audioProperties);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ColorBusterWand();
|
||||||
|
});
|
130
examples/example/games/color_busters/createColorBusterCubes.js
Normal file
130
examples/example/games/color_busters/createColorBusterCubes.js
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
//
|
||||||
|
// createColorBusterCubes.js
|
||||||
|
//
|
||||||
|
// Created by James B. Pollack @imgntn on 11/2/2015
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This script creates cubes that can be removed with a Color Buster wand.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
var DELETE_AT_ENDING = false;
|
||||||
|
|
||||||
|
var CUBE_DIMENSIONS = {
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
z: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
var NUMBER_OF_CUBES_PER_SIDE = 8;
|
||||||
|
|
||||||
|
var STARTING_CORNER_POSITION = {
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
z: 100
|
||||||
|
};
|
||||||
|
var STARTING_COLORS = [
|
||||||
|
['red', {
|
||||||
|
red: 255,
|
||||||
|
green: 0,
|
||||||
|
blue: 0
|
||||||
|
}],
|
||||||
|
['yellow', {
|
||||||
|
red: 255,
|
||||||
|
green: 255,
|
||||||
|
blue: 0
|
||||||
|
}],
|
||||||
|
['blue', {
|
||||||
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 255
|
||||||
|
}],
|
||||||
|
['orange', {
|
||||||
|
red: 255,
|
||||||
|
green: 165,
|
||||||
|
blue: 0
|
||||||
|
}],
|
||||||
|
['violet', {
|
||||||
|
red: 128,
|
||||||
|
green: 0,
|
||||||
|
blue: 128
|
||||||
|
}],
|
||||||
|
['green', {
|
||||||
|
red: 0,
|
||||||
|
green: 255,
|
||||||
|
blue: 0
|
||||||
|
}]
|
||||||
|
];
|
||||||
|
|
||||||
|
function chooseStartingColor() {
|
||||||
|
var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)];
|
||||||
|
return startingColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cubes = [];
|
||||||
|
|
||||||
|
function createColorBusterCube(row, column, vertical) {
|
||||||
|
|
||||||
|
print('make cube at ' + row + ':' + column + ":" + vertical);
|
||||||
|
|
||||||
|
var position = {
|
||||||
|
x: STARTING_CORNER_POSITION.x + row,
|
||||||
|
y: STARTING_CORNER_POSITION.y + vertical,
|
||||||
|
z: STARTING_CORNER_POSITION.z + column
|
||||||
|
};
|
||||||
|
|
||||||
|
var startingColor = chooseStartingColor();
|
||||||
|
var colorBusterCubeProperties = {
|
||||||
|
name: 'Hifi-ColorBusterCube',
|
||||||
|
type: 'Box',
|
||||||
|
dimensions: CUBE_DIMENSIONS,
|
||||||
|
collisionsWillMove: false,
|
||||||
|
ignoreForCollisions: false,
|
||||||
|
color: startingColor[1],
|
||||||
|
position: position,
|
||||||
|
userData: JSON.stringify({
|
||||||
|
hifiColorBusterCubeKey: {
|
||||||
|
originalColorName: startingColor[0]
|
||||||
|
},
|
||||||
|
grabbableKey: {
|
||||||
|
grabbable: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
var cube = Entities.addEntity(colorBusterCubeProperties);
|
||||||
|
cubes.push(cube);
|
||||||
|
return cube
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBoard() {
|
||||||
|
var vertical;
|
||||||
|
var row;
|
||||||
|
var column;
|
||||||
|
for (vertical = 0; vertical < NUMBER_OF_CUBES_PER_SIDE; vertical++) {
|
||||||
|
print('vertical:' + vertical)
|
||||||
|
//create a single layer
|
||||||
|
for (row = 0; row < NUMBER_OF_CUBES_PER_SIDE; row++) {
|
||||||
|
print('row:' + row)
|
||||||
|
for (column = 0; column < NUMBER_OF_CUBES_PER_SIDE; column++) {
|
||||||
|
print('column:' + column)
|
||||||
|
createColorBusterCube(row, column, vertical)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCubes() {
|
||||||
|
while (cubes.length > 0) {
|
||||||
|
Entities.deleteEntity(cubes.pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DELETE_AT_ENDING === true) {
|
||||||
|
Script.scriptEnding.connect(deleteCubes);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
createBoard();
|
|
@ -0,0 +1,99 @@
|
||||||
|
//
|
||||||
|
// createColorBusterWand.js
|
||||||
|
//
|
||||||
|
// Created by James B. Pollack @imgntn on 11/2/2015
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This script creates a wand that can be used to remove color buster blocks. Touch your wand to someone else's to combine colors.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
var DELETE_AT_ENDING = false;
|
||||||
|
|
||||||
|
var COLOR_WAND_MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/color_busters/wand.fbx';
|
||||||
|
var COLOR_WAND_COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/color_busters/wand_collision_hull.obj';
|
||||||
|
var COLOR_WAND_SCRIPT_URL = Script.resolvePath('colorBusterWand.js');
|
||||||
|
|
||||||
|
var COLOR_WAND_DIMENSIONS = {
|
||||||
|
x: 0.04,
|
||||||
|
y: 0.87,
|
||||||
|
z: 0.04
|
||||||
|
};
|
||||||
|
|
||||||
|
var COLOR_WAND_START_POSITION = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var STARTING_COLORS = [
|
||||||
|
['red', {
|
||||||
|
red: 255,
|
||||||
|
green: 0,
|
||||||
|
blue: 0
|
||||||
|
}],
|
||||||
|
['yellow', {
|
||||||
|
red: 255,
|
||||||
|
green: 255,
|
||||||
|
blue: 0
|
||||||
|
}],
|
||||||
|
['blue', {
|
||||||
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 255
|
||||||
|
}]
|
||||||
|
];
|
||||||
|
|
||||||
|
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||||
|
x: 0,
|
||||||
|
y: 0.5,
|
||||||
|
z: 0
|
||||||
|
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||||
|
|
||||||
|
|
||||||
|
function chooseStartingColor() {
|
||||||
|
var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)];
|
||||||
|
return startingColor
|
||||||
|
}
|
||||||
|
|
||||||
|
var wand;
|
||||||
|
|
||||||
|
function createColorBusterWand() {
|
||||||
|
var startingColor = chooseStartingColor();
|
||||||
|
var colorBusterWandProperties = {
|
||||||
|
name: 'Hifi-ColorBusterWand',
|
||||||
|
type: 'Model',
|
||||||
|
modelURL: COLOR_WAND_MODEL_URL,
|
||||||
|
shapeType: 'compound',
|
||||||
|
compoundShapeURL: COLOR_WAND_COLLISION_HULL_URL,
|
||||||
|
dimensions: COLOR_WAND_DIMENSIONS,
|
||||||
|
position: center,
|
||||||
|
script: COLOR_WAND_SCRIPT_URL,
|
||||||
|
collisionsWillMove: true,
|
||||||
|
userData: JSON.stringify({
|
||||||
|
hifiColorBusterWandKey: {
|
||||||
|
owner: MyAvatar.sessionUUID,
|
||||||
|
currentColor: startingColor[0],
|
||||||
|
originalColorName: startingColor[0],
|
||||||
|
colorLocked: false
|
||||||
|
},
|
||||||
|
grabbableKey: {
|
||||||
|
invertSolidWhileHeld: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
wand = Entities.addEntity(colorBusterWandProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteWand() {
|
||||||
|
Entities.deleteEntity(wand);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DELETE_AT_ENDING === true) {
|
||||||
|
Script.scriptEnding.connect(deleteWand);
|
||||||
|
}
|
||||||
|
|
||||||
|
createColorBusterWand();
|
|
@ -15,7 +15,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
Script.include('../utilities/tools/vector.js');
|
Script.include('../../utilities/tools/vector.js');
|
||||||
|
|
||||||
var URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/";
|
var URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/";
|
||||||
|
|
||||||
|
|
743
examples/libraries/easyStar.js
Normal file
743
examples/libraries/easyStar.js
Normal file
|
@ -0,0 +1,743 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
|
||||||
|
// Copyright (c) 2012-2015 Bryce Neal
|
||||||
|
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
// Adapted for High Fidelity by James B. Pollack on 11/6/2015
|
||||||
|
|
||||||
|
loadEasyStar = function() {
|
||||||
|
var ezStar = eStar();
|
||||||
|
return new ezStar.js()
|
||||||
|
}
|
||||||
|
|
||||||
|
var eStar = function() {
|
||||||
|
|
||||||
|
var EasyStar = EasyStar || {};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple Node that represents a single tile on the grid.
|
||||||
|
* @param {Object} parent The parent node.
|
||||||
|
* @param {Number} x The x position on the grid.
|
||||||
|
* @param {Number} y The y position on the grid.
|
||||||
|
* @param {Number} costSoFar How far this node is in moves*cost from the start.
|
||||||
|
* @param {Number} simpleDistanceToTarget Manhatten distance to the end point.
|
||||||
|
**/
|
||||||
|
EasyStar.Node = function(parent, x, y, costSoFar, simpleDistanceToTarget) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.costSoFar = costSoFar;
|
||||||
|
this.simpleDistanceToTarget = simpleDistanceToTarget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Number} Best guess distance of a cost using this node.
|
||||||
|
**/
|
||||||
|
this.bestGuessDistance = function() {
|
||||||
|
return this.costSoFar + this.simpleDistanceToTarget;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
EasyStar.Node.OPEN_LIST = 0;
|
||||||
|
EasyStar.Node.CLOSED_LIST = 1;
|
||||||
|
/**
|
||||||
|
* This is an improved Priority Queue data type implementation that can be used to sort any object type.
|
||||||
|
* It uses a technique called a binary heap.
|
||||||
|
*
|
||||||
|
* For more on binary heaps see: http://en.wikipedia.org/wiki/Binary_heap
|
||||||
|
*
|
||||||
|
* @param {String} criteria The criteria by which to sort the objects.
|
||||||
|
* This should be a property of the objects you're sorting.
|
||||||
|
*
|
||||||
|
* @param {Number} heapType either PriorityQueue.MAX_HEAP or PriorityQueue.MIN_HEAP.
|
||||||
|
**/
|
||||||
|
EasyStar.PriorityQueue = function(criteria, heapType) {
|
||||||
|
this.length = 0; //The current length of heap.
|
||||||
|
var queue = [];
|
||||||
|
var isMax = false;
|
||||||
|
|
||||||
|
//Constructor
|
||||||
|
if (heapType == EasyStar.PriorityQueue.MAX_HEAP) {
|
||||||
|
isMax = true;
|
||||||
|
} else if (heapType == EasyStar.PriorityQueue.MIN_HEAP) {
|
||||||
|
isMax = false;
|
||||||
|
} else {
|
||||||
|
throw heapType + " not supported.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the value into the heap and sorts it.
|
||||||
|
*
|
||||||
|
* @param value The object to insert into the heap.
|
||||||
|
**/
|
||||||
|
this.insert = function(value) {
|
||||||
|
if (!value.hasOwnProperty(criteria)) {
|
||||||
|
throw "Cannot insert " + value + " because it does not have a property by the name of " + criteria + ".";
|
||||||
|
}
|
||||||
|
queue.push(value);
|
||||||
|
this.length++;
|
||||||
|
bubbleUp(this.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peeks at the highest priority element.
|
||||||
|
*
|
||||||
|
* @return the highest priority element
|
||||||
|
**/
|
||||||
|
this.getHighestPriorityElement = function() {
|
||||||
|
return queue[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes and returns the highest priority element from the queue.
|
||||||
|
*
|
||||||
|
* @return the highest priority element
|
||||||
|
**/
|
||||||
|
this.shiftHighestPriorityElement = function() {
|
||||||
|
if (this.length === 0) {
|
||||||
|
throw ("There are no more elements in your priority queue.");
|
||||||
|
} else if (this.length === 1) {
|
||||||
|
var onlyValue = queue[0];
|
||||||
|
queue = [];
|
||||||
|
this.length = 0;
|
||||||
|
return onlyValue;
|
||||||
|
}
|
||||||
|
var oldRoot = queue[0];
|
||||||
|
var newRoot = queue.pop();
|
||||||
|
this.length--;
|
||||||
|
queue[0] = newRoot;
|
||||||
|
swapUntilQueueIsCorrect(0);
|
||||||
|
return oldRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bubbleUp = function(index) {
|
||||||
|
if (index === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var parent = getParentOf(index);
|
||||||
|
if (evaluate(index, parent)) {
|
||||||
|
swap(index, parent);
|
||||||
|
bubbleUp(parent);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var swapUntilQueueIsCorrect = function(value) {
|
||||||
|
var left = getLeftOf(value);
|
||||||
|
var right = getRightOf(value);
|
||||||
|
if (evaluate(left, value)) {
|
||||||
|
swap(value, left);
|
||||||
|
swapUntilQueueIsCorrect(left);
|
||||||
|
} else if (evaluate(right, value)) {
|
||||||
|
swap(value, right);
|
||||||
|
swapUntilQueueIsCorrect(right);
|
||||||
|
} else if (value == 0) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
swapUntilQueueIsCorrect(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var swap = function(self, target) {
|
||||||
|
var placeHolder = queue[self];
|
||||||
|
queue[self] = queue[target];
|
||||||
|
queue[target] = placeHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
var evaluate = function(self, target) {
|
||||||
|
if (queue[target] === undefined || queue[self] === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selfValue;
|
||||||
|
var targetValue;
|
||||||
|
|
||||||
|
// Check if the criteria should be the result of a function call.
|
||||||
|
if (typeof queue[self][criteria] === 'function') {
|
||||||
|
selfValue = queue[self][criteria]();
|
||||||
|
targetValue = queue[target][criteria]();
|
||||||
|
} else {
|
||||||
|
selfValue = queue[self][criteria];
|
||||||
|
targetValue = queue[target][criteria];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMax) {
|
||||||
|
if (selfValue > targetValue) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (selfValue < targetValue) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var getParentOf = function(index) {
|
||||||
|
return Math.floor((index - 1) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var getLeftOf = function(index) {
|
||||||
|
return index * 2 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var getRightOf = function(index) {
|
||||||
|
return index * 2 + 2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
EasyStar.PriorityQueue.MAX_HEAP = 0;
|
||||||
|
EasyStar.PriorityQueue.MIN_HEAP = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single instance of EasyStar.
|
||||||
|
* A path that is in the queue to eventually be found.
|
||||||
|
*/
|
||||||
|
EasyStar.instance = function() {
|
||||||
|
this.isDoneCalculating = true;
|
||||||
|
this.pointsToAvoid = {};
|
||||||
|
this.startX;
|
||||||
|
this.callback;
|
||||||
|
this.startY;
|
||||||
|
this.endX;
|
||||||
|
this.endY;
|
||||||
|
this.nodeHash = {};
|
||||||
|
this.openList;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* EasyStar.js
|
||||||
|
* github.com/prettymuchbryce/EasyStarJS
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
*
|
||||||
|
* Implementation By Bryce Neal (@prettymuchbryce)
|
||||||
|
**/
|
||||||
|
EasyStar.js = function() {
|
||||||
|
var STRAIGHT_COST = 1.0;
|
||||||
|
var DIAGONAL_COST = 1.4;
|
||||||
|
var syncEnabled = false;
|
||||||
|
var pointsToAvoid = {};
|
||||||
|
var collisionGrid;
|
||||||
|
var costMap = {};
|
||||||
|
var pointsToCost = {};
|
||||||
|
var allowCornerCutting = true;
|
||||||
|
var iterationsSoFar;
|
||||||
|
var instances = [];
|
||||||
|
var iterationsPerCalculation = Number.MAX_VALUE;
|
||||||
|
var acceptableTiles;
|
||||||
|
var diagonalsEnabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the collision grid that EasyStar uses.
|
||||||
|
*
|
||||||
|
* @param {Array|Number} tiles An array of numbers that represent
|
||||||
|
* which tiles in your grid should be considered
|
||||||
|
* acceptable, or "walkable".
|
||||||
|
**/
|
||||||
|
this.setAcceptableTiles = function(tiles) {
|
||||||
|
if (tiles instanceof Array) {
|
||||||
|
// Array
|
||||||
|
acceptableTiles = tiles;
|
||||||
|
} else if (!isNaN(parseFloat(tiles)) && isFinite(tiles)) {
|
||||||
|
// Number
|
||||||
|
acceptableTiles = [tiles];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables sync mode for this EasyStar instance..
|
||||||
|
* if you're into that sort of thing.
|
||||||
|
**/
|
||||||
|
this.enableSync = function() {
|
||||||
|
syncEnabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables sync mode for this EasyStar instance.
|
||||||
|
**/
|
||||||
|
this.disableSync = function() {
|
||||||
|
syncEnabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable diagonal pathfinding.
|
||||||
|
*/
|
||||||
|
this.enableDiagonals = function() {
|
||||||
|
diagonalsEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable diagonal pathfinding.
|
||||||
|
*/
|
||||||
|
this.disableDiagonals = function() {
|
||||||
|
diagonalsEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the collision grid that EasyStar uses.
|
||||||
|
*
|
||||||
|
* @param {Array} grid The collision grid that this EasyStar instance will read from.
|
||||||
|
* This should be a 2D Array of Numbers.
|
||||||
|
**/
|
||||||
|
this.setGrid = function(grid) {
|
||||||
|
collisionGrid = grid;
|
||||||
|
|
||||||
|
//Setup cost map
|
||||||
|
for (var y = 0; y < collisionGrid.length; y++) {
|
||||||
|
for (var x = 0; x < collisionGrid[0].length; x++) {
|
||||||
|
if (!costMap[collisionGrid[y][x]]) {
|
||||||
|
costMap[collisionGrid[y][x]] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tile cost for a particular tile type.
|
||||||
|
*
|
||||||
|
* @param {Number} The tile type to set the cost for.
|
||||||
|
* @param {Number} The multiplicative cost associated with the given tile.
|
||||||
|
**/
|
||||||
|
this.setTileCost = function(tileType, cost) {
|
||||||
|
costMap[tileType] = cost;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the an additional cost for a particular point.
|
||||||
|
* Overrides the cost from setTileCost.
|
||||||
|
*
|
||||||
|
* @param {Number} x The x value of the point to cost.
|
||||||
|
* @param {Number} y The y value of the point to cost.
|
||||||
|
* @param {Number} The multiplicative cost associated with the given point.
|
||||||
|
**/
|
||||||
|
this.setAdditionalPointCost = function(x, y, cost) {
|
||||||
|
pointsToCost[x + '_' + y] = cost;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the additional cost for a particular point.
|
||||||
|
*
|
||||||
|
* @param {Number} x The x value of the point to stop costing.
|
||||||
|
* @param {Number} y The y value of the point to stop costing.
|
||||||
|
**/
|
||||||
|
this.removeAdditionalPointCost = function(x, y) {
|
||||||
|
delete pointsToCost[x + '_' + y];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all additional point costs.
|
||||||
|
**/
|
||||||
|
this.removeAllAdditionalPointCosts = function() {
|
||||||
|
pointsToCost = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of search iterations per calculation.
|
||||||
|
* A lower number provides a slower result, but more practical if you
|
||||||
|
* have a large tile-map and don't want to block your thread while
|
||||||
|
* finding a path.
|
||||||
|
*
|
||||||
|
* @param {Number} iterations The number of searches to prefrom per calculate() call.
|
||||||
|
**/
|
||||||
|
this.setIterationsPerCalculation = function(iterations) {
|
||||||
|
iterationsPerCalculation = iterations;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Avoid a particular point on the grid,
|
||||||
|
* regardless of whether or not it is an acceptable tile.
|
||||||
|
*
|
||||||
|
* @param {Number} x The x value of the point to avoid.
|
||||||
|
* @param {Number} y The y value of the point to avoid.
|
||||||
|
**/
|
||||||
|
this.avoidAdditionalPoint = function(x, y) {
|
||||||
|
pointsToAvoid[x + "_" + y] = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop avoiding a particular point on the grid.
|
||||||
|
*
|
||||||
|
* @param {Number} x The x value of the point to stop avoiding.
|
||||||
|
* @param {Number} y The y value of the point to stop avoiding.
|
||||||
|
**/
|
||||||
|
this.stopAvoidingAdditionalPoint = function(x, y) {
|
||||||
|
delete pointsToAvoid[x + "_" + y];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables corner cutting in diagonal movement.
|
||||||
|
**/
|
||||||
|
this.enableCornerCutting = function() {
|
||||||
|
allowCornerCutting = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables corner cutting in diagonal movement.
|
||||||
|
**/
|
||||||
|
this.disableCornerCutting = function() {
|
||||||
|
allowCornerCutting = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop avoiding all additional points on the grid.
|
||||||
|
**/
|
||||||
|
this.stopAvoidingAllAdditionalPoints = function() {
|
||||||
|
pointsToAvoid = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a path.
|
||||||
|
*
|
||||||
|
* @param {Number} startX The X position of the starting point.
|
||||||
|
* @param {Number} startY The Y position of the starting point.
|
||||||
|
* @param {Number} endX The X position of the ending point.
|
||||||
|
* @param {Number} endY The Y position of the ending point.
|
||||||
|
* @param {Function} callback A function that is called when your path
|
||||||
|
* is found, or no path is found.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
this.findPath = function(startX, startY, endX, endY, callback) {
|
||||||
|
// Wraps the callback for sync vs async logic
|
||||||
|
var callbackWrapper = function(result) {
|
||||||
|
if (syncEnabled) {
|
||||||
|
callback(result);
|
||||||
|
} else {
|
||||||
|
Script.setTimeout(function() {
|
||||||
|
callback(result);
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No acceptable tiles were set
|
||||||
|
if (acceptableTiles === undefined) {
|
||||||
|
throw new Error("You can't set a path without first calling setAcceptableTiles() on EasyStar.");
|
||||||
|
}
|
||||||
|
// No grid was set
|
||||||
|
if (collisionGrid === undefined) {
|
||||||
|
throw new Error("You can't set a path without first calling setGrid() on EasyStar.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start or endpoint outside of scope.
|
||||||
|
if (startX < 0 || startY < 0 || endX < 0 || endX < 0 ||
|
||||||
|
startX > collisionGrid[0].length - 1 || startY > collisionGrid.length - 1 ||
|
||||||
|
endX > collisionGrid[0].length - 1 || endY > collisionGrid.length - 1) {
|
||||||
|
throw new Error("Your start or end point is outside the scope of your grid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start and end are the same tile.
|
||||||
|
if (startX === endX && startY === endY) {
|
||||||
|
callbackWrapper([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End point is not an acceptable tile.
|
||||||
|
var endTile = collisionGrid[endY][endX];
|
||||||
|
var isAcceptable = false;
|
||||||
|
for (var i = 0; i < acceptableTiles.length; i++) {
|
||||||
|
if (endTile === acceptableTiles[i]) {
|
||||||
|
isAcceptable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAcceptable === false) {
|
||||||
|
callbackWrapper(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the instance
|
||||||
|
var instance = new EasyStar.instance();
|
||||||
|
instance.openList = new EasyStar.PriorityQueue("bestGuessDistance", EasyStar.PriorityQueue.MIN_HEAP);
|
||||||
|
instance.isDoneCalculating = false;
|
||||||
|
instance.nodeHash = {};
|
||||||
|
instance.startX = startX;
|
||||||
|
instance.startY = startY;
|
||||||
|
instance.endX = endX;
|
||||||
|
instance.endY = endY;
|
||||||
|
instance.callback = callbackWrapper;
|
||||||
|
|
||||||
|
instance.openList.insert(coordinateToNode(instance, instance.startX,
|
||||||
|
instance.startY, null, STRAIGHT_COST));
|
||||||
|
|
||||||
|
instances.push(instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method steps through the A* Algorithm in an attempt to
|
||||||
|
* find your path(s). It will search 4-8 tiles (depending on diagonals) for every calculation.
|
||||||
|
* You can change the number of calculations done in a call by using
|
||||||
|
* easystar.setIteratonsPerCalculation().
|
||||||
|
**/
|
||||||
|
this.calculate = function() {
|
||||||
|
if (instances.length === 0 || collisionGrid === undefined || acceptableTiles === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (iterationsSoFar = 0; iterationsSoFar < iterationsPerCalculation; iterationsSoFar++) {
|
||||||
|
if (instances.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncEnabled) {
|
||||||
|
// If this is a sync instance, we want to make sure that it calculates synchronously.
|
||||||
|
iterationsSoFar = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Couldn't find a path.
|
||||||
|
if (instances[0].openList.length === 0) {
|
||||||
|
var ic = instances[0];
|
||||||
|
ic.callback(null);
|
||||||
|
instances.shift();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var searchNode = instances[0].openList.shiftHighestPriorityElement();
|
||||||
|
|
||||||
|
var tilesToSearch = [];
|
||||||
|
searchNode.list = EasyStar.Node.CLOSED_LIST;
|
||||||
|
|
||||||
|
if (searchNode.y > 0) {
|
||||||
|
tilesToSearch.push({
|
||||||
|
instance: instances[0],
|
||||||
|
searchNode: searchNode,
|
||||||
|
x: 0,
|
||||||
|
y: -1,
|
||||||
|
cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y - 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (searchNode.x < collisionGrid[0].length - 1) {
|
||||||
|
tilesToSearch.push({
|
||||||
|
instance: instances[0],
|
||||||
|
searchNode: searchNode,
|
||||||
|
x: 1,
|
||||||
|
y: 0,
|
||||||
|
cost: STRAIGHT_COST * getTileCost(searchNode.x + 1, searchNode.y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (searchNode.y < collisionGrid.length - 1) {
|
||||||
|
tilesToSearch.push({
|
||||||
|
instance: instances[0],
|
||||||
|
searchNode: searchNode,
|
||||||
|
x: 0,
|
||||||
|
y: 1,
|
||||||
|
cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y + 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (searchNode.x > 0) {
|
||||||
|
tilesToSearch.push({
|
||||||
|
instance: instances[0],
|
||||||
|
searchNode: searchNode,
|
||||||
|
x: -1,
|
||||||
|
y: 0,
|
||||||
|
cost: STRAIGHT_COST * getTileCost(searchNode.x - 1, searchNode.y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (diagonalsEnabled) {
|
||||||
|
if (searchNode.x > 0 && searchNode.y > 0) {
|
||||||
|
|
||||||
|
if (allowCornerCutting ||
|
||||||
|
(isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y - 1) &&
|
||||||
|
isTileWalkable(collisionGrid, acceptableTiles, searchNode.x - 1, searchNode.y))) {
|
||||||
|
|
||||||
|
tilesToSearch.push({
|
||||||
|
instance: instances[0],
|
||||||
|
searchNode: searchNode,
|
||||||
|
x: -1,
|
||||||
|
y: -1,
|
||||||
|
cost: DIAGONAL_COST * getTileCost(searchNode.x - 1, searchNode.y - 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchNode.x < collisionGrid[0].length - 1 && searchNode.y < collisionGrid.length - 1) {
|
||||||
|
|
||||||
|
if (allowCornerCutting ||
|
||||||
|
(isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y + 1) &&
|
||||||
|
isTileWalkable(collisionGrid, acceptableTiles, searchNode.x + 1, searchNode.y))) {
|
||||||
|
|
||||||
|
tilesToSearch.push({
|
||||||
|
instance: instances[0],
|
||||||
|
searchNode: searchNode,
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
cost: DIAGONAL_COST * getTileCost(searchNode.x + 1, searchNode.y + 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchNode.x < collisionGrid[0].length - 1 && searchNode.y > 0) {
|
||||||
|
|
||||||
|
if (allowCornerCutting ||
|
||||||
|
(isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y - 1) &&
|
||||||
|
isTileWalkable(collisionGrid, acceptableTiles, searchNode.x + 1, searchNode.y))) {
|
||||||
|
|
||||||
|
|
||||||
|
tilesToSearch.push({
|
||||||
|
instance: instances[0],
|
||||||
|
searchNode: searchNode,
|
||||||
|
x: 1,
|
||||||
|
y: -1,
|
||||||
|
cost: DIAGONAL_COST * getTileCost(searchNode.x + 1, searchNode.y - 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchNode.x > 0 && searchNode.y < collisionGrid.length - 1) {
|
||||||
|
|
||||||
|
if (allowCornerCutting ||
|
||||||
|
(isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y + 1) &&
|
||||||
|
isTileWalkable(collisionGrid, acceptableTiles, searchNode.x - 1, searchNode.y))) {
|
||||||
|
|
||||||
|
|
||||||
|
tilesToSearch.push({
|
||||||
|
instance: instances[0],
|
||||||
|
searchNode: searchNode,
|
||||||
|
x: -1,
|
||||||
|
y: 1,
|
||||||
|
cost: DIAGONAL_COST * getTileCost(searchNode.x - 1, searchNode.y + 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First sort all of the potential nodes we could search by their cost + heuristic distance.
|
||||||
|
tilesToSearch.sort(function(a, b) {
|
||||||
|
var aCost = a.cost + getDistance(searchNode.x + a.x, searchNode.y + a.y, instances[0].endX, instances[0].endY)
|
||||||
|
var bCost = b.cost + getDistance(searchNode.x + b.x, searchNode.y + b.y, instances[0].endX, instances[0].endY)
|
||||||
|
if (aCost < bCost) {
|
||||||
|
return -1;
|
||||||
|
} else if (aCost === bCost) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var isDoneCalculating = false;
|
||||||
|
|
||||||
|
// Search all of the surrounding nodes
|
||||||
|
for (var i = 0; i < tilesToSearch.length; i++) {
|
||||||
|
checkAdjacentNode(tilesToSearch[i].instance, tilesToSearch[i].searchNode,
|
||||||
|
tilesToSearch[i].x, tilesToSearch[i].y, tilesToSearch[i].cost);
|
||||||
|
if (tilesToSearch[i].instance.isDoneCalculating === true) {
|
||||||
|
isDoneCalculating = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDoneCalculating) {
|
||||||
|
instances.shift();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Private methods follow
|
||||||
|
var checkAdjacentNode = function(instance, searchNode, x, y, cost) {
|
||||||
|
var adjacentCoordinateX = searchNode.x + x;
|
||||||
|
var adjacentCoordinateY = searchNode.y + y;
|
||||||
|
|
||||||
|
if (pointsToAvoid[adjacentCoordinateX + "_" + adjacentCoordinateY] === undefined) {
|
||||||
|
// Handles the case where we have found the destination
|
||||||
|
if (instance.endX === adjacentCoordinateX && instance.endY === adjacentCoordinateY) {
|
||||||
|
instance.isDoneCalculating = true;
|
||||||
|
var path = [];
|
||||||
|
var pathLen = 0;
|
||||||
|
path[pathLen] = {
|
||||||
|
x: adjacentCoordinateX,
|
||||||
|
y: adjacentCoordinateY
|
||||||
|
};
|
||||||
|
pathLen++;
|
||||||
|
path[pathLen] = {
|
||||||
|
x: searchNode.x,
|
||||||
|
y: searchNode.y
|
||||||
|
};
|
||||||
|
pathLen++;
|
||||||
|
var parent = searchNode.parent;
|
||||||
|
while (parent != null) {
|
||||||
|
path[pathLen] = {
|
||||||
|
x: parent.x,
|
||||||
|
y: parent.y
|
||||||
|
};
|
||||||
|
pathLen++;
|
||||||
|
parent = parent.parent;
|
||||||
|
}
|
||||||
|
path.reverse();
|
||||||
|
var ic = instance;
|
||||||
|
var ip = path;
|
||||||
|
ic.callback(ip);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTileWalkable(collisionGrid, acceptableTiles, adjacentCoordinateX, adjacentCoordinateY)) {
|
||||||
|
var node = coordinateToNode(instance, adjacentCoordinateX,
|
||||||
|
adjacentCoordinateY, searchNode, cost);
|
||||||
|
|
||||||
|
if (node.list === undefined) {
|
||||||
|
node.list = EasyStar.Node.OPEN_LIST;
|
||||||
|
instance.openList.insert(node);
|
||||||
|
} else if (node.list === EasyStar.Node.OPEN_LIST) {
|
||||||
|
if (searchNode.costSoFar + cost < node.costSoFar) {
|
||||||
|
node.costSoFar = searchNode.costSoFar + cost;
|
||||||
|
node.parent = searchNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
var isTileWalkable = function(collisionGrid, acceptableTiles, x, y) {
|
||||||
|
for (var i = 0; i < acceptableTiles.length; i++) {
|
||||||
|
if (collisionGrid[y][x] === acceptableTiles[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getTileCost = function(x, y) {
|
||||||
|
return pointsToCost[x + '_' + y] || costMap[collisionGrid[y][x]]
|
||||||
|
};
|
||||||
|
|
||||||
|
var coordinateToNode = function(instance, x, y, parent, cost) {
|
||||||
|
if (instance.nodeHash[x + "_" + y] !== undefined) {
|
||||||
|
return instance.nodeHash[x + "_" + y];
|
||||||
|
}
|
||||||
|
var simpleDistanceToTarget = getDistance(x, y, instance.endX, instance.endY);
|
||||||
|
if (parent !== null) {
|
||||||
|
var costSoFar = parent.costSoFar + cost;
|
||||||
|
} else {
|
||||||
|
costSoFar = simpleDistanceToTarget;
|
||||||
|
}
|
||||||
|
var node = new EasyStar.Node(parent, x, y, costSoFar, simpleDistanceToTarget);
|
||||||
|
instance.nodeHash[x + "_" + y] = node;
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getDistance = function(x1, y1, x2, y2) {
|
||||||
|
return Math.sqrt((x2 -= x1) * x2 + (y2 -= y1) * y2);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return EasyStar
|
||||||
|
}
|
270
examples/libraries/easyStarExample.js
Normal file
270
examples/libraries/easyStarExample.js
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
//
|
||||||
|
// easyStarExample.js
|
||||||
|
//
|
||||||
|
// Created by James B. Pollack @imgntn on 11/9/2015
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is a script that sets up a grid of obstacles and passable tiles, finds a path, and then moves an entity along the path.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
// To-Do:
|
||||||
|
// Abstract start position and make tiles, spheres, etc. relative
|
||||||
|
// Handle dynamically changing grids
|
||||||
|
|
||||||
|
Script.include('easyStar.js');
|
||||||
|
var easystar = loadEasyStar();
|
||||||
|
Script.include('tween.js');
|
||||||
|
var TWEEN = loadTween();
|
||||||
|
|
||||||
|
var ANIMATION_DURATION = 350;
|
||||||
|
var grid = [
|
||||||
|
[0, 0, 1, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 1, 1, 0, 1, 0, 1, 0, 0],
|
||||||
|
[0, 0, 1, 0, 0, 0, 0, 1, 1],
|
||||||
|
[0, 0, 1, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 1, 1, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
easystar.setGrid(grid);
|
||||||
|
|
||||||
|
easystar.setAcceptableTiles([0]);
|
||||||
|
easystar.enableCornerCutting();
|
||||||
|
easystar.findPath(0, 0, 8, 0, function(path) {
|
||||||
|
if (path === null) {
|
||||||
|
print("Path was not found.");
|
||||||
|
Script.update.disconnect(tickEasyStar);
|
||||||
|
} else {
|
||||||
|
print('path' + JSON.stringify(path));
|
||||||
|
|
||||||
|
convertPath(path);
|
||||||
|
Script.update.disconnect(tickEasyStar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var tickEasyStar = function() {
|
||||||
|
easystar.calculate();
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.update.connect(tickEasyStar);
|
||||||
|
|
||||||
|
//a sphere that will get moved
|
||||||
|
var playerSphere = Entities.addEntity({
|
||||||
|
type: 'Sphere',
|
||||||
|
shape: 'Sphere',
|
||||||
|
color: {
|
||||||
|
red: 255,
|
||||||
|
green: 0,
|
||||||
|
blue: 0
|
||||||
|
},
|
||||||
|
dimensions: {
|
||||||
|
x: 0.5,
|
||||||
|
y: 0.5,
|
||||||
|
z: 0.5
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
gravity: {
|
||||||
|
x: 0,
|
||||||
|
y: -9.8,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
collisionsWillMove: true,
|
||||||
|
linearDamping: 0.2
|
||||||
|
});
|
||||||
|
|
||||||
|
Script.setInterval(function(){
|
||||||
|
Entities.editEntity(playerSphere,{
|
||||||
|
velocity:{
|
||||||
|
x:0,
|
||||||
|
y:4,
|
||||||
|
z:0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},1000)
|
||||||
|
|
||||||
|
var sphereProperties;
|
||||||
|
|
||||||
|
//for keeping track of entities
|
||||||
|
var obstacles = [];
|
||||||
|
var passables = [];
|
||||||
|
|
||||||
|
function createPassableAtTilePosition(posX, posY) {
|
||||||
|
var properties = {
|
||||||
|
type: 'Box',
|
||||||
|
shapeType: 'Box',
|
||||||
|
dimensions: {
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
z: 1
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
x: posY,
|
||||||
|
y: -1,
|
||||||
|
z: posX
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 255
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var passable = Entities.addEntity(properties);
|
||||||
|
passables.push(passable);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function createObstacleAtTilePosition(posX, posY) {
|
||||||
|
var properties = {
|
||||||
|
type: 'Box',
|
||||||
|
shapeType: 'Box',
|
||||||
|
dimensions: {
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
z: 1
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
x: posY,
|
||||||
|
y: 0,
|
||||||
|
z: posX
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
red: 0,
|
||||||
|
green: 255,
|
||||||
|
blue: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var obstacle = Entities.addEntity(properties);
|
||||||
|
obstacles.push(obstacle);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createObstacles(grid) {
|
||||||
|
grid.forEach(function(row, rowIndex) {
|
||||||
|
row.forEach(function(v, index) {
|
||||||
|
if (v === 1) {
|
||||||
|
createObstacleAtTilePosition(rowIndex, index);
|
||||||
|
}
|
||||||
|
if (v === 0) {
|
||||||
|
createPassableAtTilePosition(rowIndex, index);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
createObstacles(grid);
|
||||||
|
|
||||||
|
|
||||||
|
var currentSpherePosition = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function convertPathPointToCoordinates(x, y) {
|
||||||
|
return {
|
||||||
|
x: y,
|
||||||
|
z: x
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var convertedPath = [];
|
||||||
|
|
||||||
|
//convert our path to Vec3s
|
||||||
|
function convertPath(path) {
|
||||||
|
path.forEach(function(point) {
|
||||||
|
var convertedPoint = convertPathPointToCoordinates(point.x, point.y);
|
||||||
|
convertedPath.push(convertedPoint);
|
||||||
|
});
|
||||||
|
createTweenPath(convertedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePosition() {
|
||||||
|
sphereProperties = Entities.getEntityProperties(playerSphere, "position");
|
||||||
|
|
||||||
|
Entities.editEntity(playerSphere, {
|
||||||
|
position: {
|
||||||
|
x: currentSpherePosition.z,
|
||||||
|
y: sphereProperties.position.y,
|
||||||
|
z: currentSpherePosition.x
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var upVelocity = {
|
||||||
|
x: 0,
|
||||||
|
y: 2.5,
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var noVelocity = {
|
||||||
|
x: 0,
|
||||||
|
y: -3.5,
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTweenPath(convertedPath) {
|
||||||
|
var i;
|
||||||
|
var stepTweens = [];
|
||||||
|
var velocityTweens = [];
|
||||||
|
|
||||||
|
//create the tweens
|
||||||
|
for (i = 0; i < convertedPath.length - 1; i++) {
|
||||||
|
var stepTween = new TWEEN.Tween(currentSpherePosition).to(convertedPath[i + 1], ANIMATION_DURATION).onUpdate(updatePosition).onComplete(tweenStep);
|
||||||
|
|
||||||
|
|
||||||
|
stepTweens.push(stepTween);
|
||||||
|
}
|
||||||
|
|
||||||
|
var j;
|
||||||
|
//chain one tween to the next
|
||||||
|
for (j = 0; j < stepTweens.length - 1; j++) {
|
||||||
|
stepTweens[j].chain(stepTweens[j + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//start the tween
|
||||||
|
stepTweens[0].start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var velocityShaper = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function tweenStep() {
|
||||||
|
// print('step between tweens')
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTweens() {
|
||||||
|
//hook tween updates into our update loop
|
||||||
|
TWEEN.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.update.connect(updateTweens);
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
while (obstacles.length > 0) {
|
||||||
|
Entities.deleteEntity(obstacles.pop());
|
||||||
|
}
|
||||||
|
while (passables.length > 0) {
|
||||||
|
Entities.deleteEntity(passables.pop());
|
||||||
|
}
|
||||||
|
Entities.deleteEntity(playerSphere);
|
||||||
|
Script.update.disconnect(updateTweens);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
|
@ -9,7 +9,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
Script.include("libraries/overlayUtils.js");
|
Script.include("overlayUtils.js");
|
||||||
|
|
||||||
var MOUSE_SENSITIVITY = 0.9;
|
var MOUSE_SENSITIVITY = 0.9;
|
||||||
var SCROLL_SENSITIVITY = 0.05;
|
var SCROLL_SENSITIVITY = 0.05;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
var ENTITY_LIST_HTML_URL = Script.resolvePath('../html/entityList.html');
|
||||||
|
|
||||||
EntityListTool = function(opts) {
|
EntityListTool = function(opts) {
|
||||||
var that = {};
|
var that = {};
|
||||||
|
|
||||||
var url = Script.resolvePath('html/entityList.html');
|
var url = ENTITY_LIST_HTML_URL;
|
||||||
var webView = new WebWindow('Entities', url, 200, 280, true);
|
var webView = new WebWindow('Entities', url, 200, 280, true);
|
||||||
|
|
||||||
var searchRadius = 100;
|
var searchRadius = 100;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
var GRID_CONTROLS_HTML_URL = Script.resolvePath('../html/gridControls.html');
|
||||||
|
|
||||||
Grid = function(opts) {
|
Grid = function(opts) {
|
||||||
var that = {};
|
var that = {};
|
||||||
|
|
||||||
|
@ -228,7 +230,7 @@ GridTool = function(opts) {
|
||||||
var verticalGrid = opts.verticalGrid;
|
var verticalGrid = opts.verticalGrid;
|
||||||
var listeners = [];
|
var listeners = [];
|
||||||
|
|
||||||
var url = Script.resolvePath('html/gridControls.html');
|
var url = GRID_CONTROLS_HTML_URL;
|
||||||
var webView = new WebWindow('Grid', url, 200, 280, true);
|
var webView = new WebWindow('Grid', url, 200, 280, true);
|
||||||
|
|
||||||
horizontalGrid.addListener(function(data) {
|
horizontalGrid.addListener(function(data) {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
Script.include("../toys/breakdanceCore.js");
|
Script.include("../../../breakdanceCore.js");
|
||||||
|
|
||||||
OmniToolModules.Breakdance = function() {
|
OmniToolModules.Breakdance = function() {
|
||||||
print("OmniToolModules.Breakdance...");
|
print("OmniToolModules.Breakdance...");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
Script.include("avatarRelativeOverlays.js");
|
Script.include("../../avatarRelativeOverlays.js");
|
||||||
|
|
||||||
OmniToolModules.Test = function(omniTool, activeEntityId) {
|
OmniToolModules.Test = function(omniTool, activeEntityId) {
|
||||||
this.omniTool = omniTool;
|
this.omniTool = omniTool;
|
||||||
|
|
878
examples/libraries/tween.js
Normal file
878
examples/libraries/tween.js
Normal file
|
@ -0,0 +1,878 @@
|
||||||
|
/**
|
||||||
|
* Tween.js - Licensed under the MIT license
|
||||||
|
* https://github.com/tweenjs/tween.js
|
||||||
|
* ----------------------------------------------
|
||||||
|
*
|
||||||
|
* See https://github.com/tweenjs/tween.js/graphs/contributors for the full list of contributors.
|
||||||
|
* Thank you all, you're awesome!
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Include a performance.now polyfill
|
||||||
|
(function () {
|
||||||
|
window= {}
|
||||||
|
if ('performance' in window === false) {
|
||||||
|
window.performance = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// IE 8
|
||||||
|
Date.now = (Date.now || function () {
|
||||||
|
return new Date().getTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
if ('now' in window.performance === false) {
|
||||||
|
var offset = window.performance.timing && window.performance.timing.navigationStart ? window.performance.timing.navigationStart
|
||||||
|
: Date.now();
|
||||||
|
|
||||||
|
window.performance.now = function () {
|
||||||
|
return Date.now() - offset;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
var TWEEN = TWEEN || (function () {
|
||||||
|
|
||||||
|
var _tweens = [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
getAll: function () {
|
||||||
|
|
||||||
|
return _tweens;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAll: function () {
|
||||||
|
|
||||||
|
_tweens = [];
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
add: function (tween) {
|
||||||
|
|
||||||
|
_tweens.push(tween);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function (tween) {
|
||||||
|
|
||||||
|
var i = _tweens.indexOf(tween);
|
||||||
|
|
||||||
|
if (i !== -1) {
|
||||||
|
_tweens.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function (time) {
|
||||||
|
|
||||||
|
if (_tweens.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
time = time !== undefined ? time : window.performance.now();
|
||||||
|
|
||||||
|
while (i < _tweens.length) {
|
||||||
|
|
||||||
|
if (_tweens[i].update(time)) {
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
_tweens.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
TWEEN.Tween = function (object) {
|
||||||
|
|
||||||
|
var _object = object;
|
||||||
|
var _valuesStart = {};
|
||||||
|
var _valuesEnd = {};
|
||||||
|
var _valuesStartRepeat = {};
|
||||||
|
var _duration = 1000;
|
||||||
|
var _repeat = 0;
|
||||||
|
var _yoyo = false;
|
||||||
|
var _isPlaying = false;
|
||||||
|
var _reversed = false;
|
||||||
|
var _delayTime = 0;
|
||||||
|
var _startTime = null;
|
||||||
|
var _easingFunction = TWEEN.Easing.Linear.None;
|
||||||
|
var _interpolationFunction = TWEEN.Interpolation.Linear;
|
||||||
|
var _chainedTweens = [];
|
||||||
|
var _onStartCallback = null;
|
||||||
|
var _onStartCallbackFired = false;
|
||||||
|
var _onUpdateCallback = null;
|
||||||
|
var _onCompleteCallback = null;
|
||||||
|
var _onStopCallback = null;
|
||||||
|
|
||||||
|
// Set all starting values present on the target object
|
||||||
|
for (var field in object) {
|
||||||
|
_valuesStart[field] = parseFloat(object[field], 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.to = function (properties, duration) {
|
||||||
|
|
||||||
|
if (duration !== undefined) {
|
||||||
|
_duration = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
_valuesEnd = properties;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.start = function (time) {
|
||||||
|
|
||||||
|
TWEEN.add(this);
|
||||||
|
|
||||||
|
_isPlaying = true;
|
||||||
|
|
||||||
|
_onStartCallbackFired = false;
|
||||||
|
|
||||||
|
_startTime = time !== undefined ? time : window.performance.now();
|
||||||
|
_startTime += _delayTime;
|
||||||
|
|
||||||
|
for (var property in _valuesEnd) {
|
||||||
|
|
||||||
|
// Check if an Array was provided as property value
|
||||||
|
if (_valuesEnd[property] instanceof Array) {
|
||||||
|
|
||||||
|
if (_valuesEnd[property].length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a local copy of the Array with the start value at the front
|
||||||
|
_valuesEnd[property] = [_object[property]].concat(_valuesEnd[property]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_valuesStart[property] = _object[property];
|
||||||
|
|
||||||
|
if ((_valuesStart[property] instanceof Array) === false) {
|
||||||
|
_valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings
|
||||||
|
}
|
||||||
|
|
||||||
|
_valuesStartRepeat[property] = _valuesStart[property] || 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stop = function () {
|
||||||
|
|
||||||
|
if (!_isPlaying) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TWEEN.remove(this);
|
||||||
|
_isPlaying = false;
|
||||||
|
|
||||||
|
if (_onStopCallback !== null) {
|
||||||
|
_onStopCallback.call(_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stopChainedTweens();
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stopChainedTweens = function () {
|
||||||
|
|
||||||
|
for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
|
||||||
|
_chainedTweens[i].stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.delay = function (amount) {
|
||||||
|
|
||||||
|
_delayTime = amount;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.repeat = function (times) {
|
||||||
|
|
||||||
|
_repeat = times;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.yoyo = function (yoyo) {
|
||||||
|
|
||||||
|
_yoyo = yoyo;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.easing = function (easing) {
|
||||||
|
|
||||||
|
_easingFunction = easing;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.interpolation = function (interpolation) {
|
||||||
|
|
||||||
|
_interpolationFunction = interpolation;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.chain = function () {
|
||||||
|
|
||||||
|
_chainedTweens = arguments;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onStart = function (callback) {
|
||||||
|
|
||||||
|
_onStartCallback = callback;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onUpdate = function (callback) {
|
||||||
|
|
||||||
|
_onUpdateCallback = callback;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onComplete = function (callback) {
|
||||||
|
|
||||||
|
_onCompleteCallback = callback;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onStop = function (callback) {
|
||||||
|
|
||||||
|
_onStopCallback = callback;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.update = function (time) {
|
||||||
|
|
||||||
|
var property;
|
||||||
|
var elapsed;
|
||||||
|
var value;
|
||||||
|
|
||||||
|
if (time < _startTime) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_onStartCallbackFired === false) {
|
||||||
|
|
||||||
|
if (_onStartCallback !== null) {
|
||||||
|
_onStartCallback.call(_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onStartCallbackFired = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed = (time - _startTime) / _duration;
|
||||||
|
elapsed = elapsed > 1 ? 1 : elapsed;
|
||||||
|
|
||||||
|
value = _easingFunction(elapsed);
|
||||||
|
|
||||||
|
for (property in _valuesEnd) {
|
||||||
|
|
||||||
|
var start = _valuesStart[property] || 0;
|
||||||
|
var end = _valuesEnd[property];
|
||||||
|
|
||||||
|
if (end instanceof Array) {
|
||||||
|
|
||||||
|
_object[property] = _interpolationFunction(end, value);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Parses relative end values with start as base (e.g.: +10, -3)
|
||||||
|
if (typeof (end) === 'string') {
|
||||||
|
end = start + parseFloat(end, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect against non numeric properties.
|
||||||
|
if (typeof (end) === 'number') {
|
||||||
|
_object[property] = start + (end - start) * value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_onUpdateCallback !== null) {
|
||||||
|
_onUpdateCallback.call(_object, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elapsed === 1) {
|
||||||
|
|
||||||
|
if (_repeat > 0) {
|
||||||
|
|
||||||
|
if (isFinite(_repeat)) {
|
||||||
|
_repeat--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reassign starting values, restart by making startTime = now
|
||||||
|
for (property in _valuesStartRepeat) {
|
||||||
|
|
||||||
|
if (typeof (_valuesEnd[property]) === 'string') {
|
||||||
|
_valuesStartRepeat[property] = _valuesStartRepeat[property] + parseFloat(_valuesEnd[property], 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_yoyo) {
|
||||||
|
var tmp = _valuesStartRepeat[property];
|
||||||
|
|
||||||
|
_valuesStartRepeat[property] = _valuesEnd[property];
|
||||||
|
_valuesEnd[property] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
_valuesStart[property] = _valuesStartRepeat[property];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_yoyo) {
|
||||||
|
_reversed = !_reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
_startTime = time + _delayTime;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (_onCompleteCallback !== null) {
|
||||||
|
_onCompleteCallback.call(_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
|
||||||
|
// Make the chained tweens start exactly at the time they should,
|
||||||
|
// even if the `update()` method was called way past the duration of the tween
|
||||||
|
_chainedTweens[i].start(_startTime + _duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TWEEN.Easing = {
|
||||||
|
|
||||||
|
Linear: {
|
||||||
|
|
||||||
|
None: function (k) {
|
||||||
|
|
||||||
|
return k;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Quadratic: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
return k * k;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
return k * (2 - k);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
if ((k *= 2) < 1) {
|
||||||
|
return 0.5 * k * k;
|
||||||
|
}
|
||||||
|
|
||||||
|
return - 0.5 * (--k * (k - 2) - 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Cubic: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
return k * k * k;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
return --k * k * k + 1;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
if ((k *= 2) < 1) {
|
||||||
|
return 0.5 * k * k * k;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.5 * ((k -= 2) * k * k + 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Quartic: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
return k * k * k * k;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
return 1 - (--k * k * k * k);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
if ((k *= 2) < 1) {
|
||||||
|
return 0.5 * k * k * k * k;
|
||||||
|
}
|
||||||
|
|
||||||
|
return - 0.5 * ((k -= 2) * k * k * k - 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Quintic: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
return k * k * k * k * k;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
return --k * k * k * k * k + 1;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
if ((k *= 2) < 1) {
|
||||||
|
return 0.5 * k * k * k * k * k;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.5 * ((k -= 2) * k * k * k * k + 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Sinusoidal: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
return 1 - Math.cos(k * Math.PI / 2);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
return Math.sin(k * Math.PI / 2);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
return 0.5 * (1 - Math.cos(Math.PI * k));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Exponential: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
return k === 0 ? 0 : Math.pow(1024, k - 1);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
if (k === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k === 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((k *= 2) < 1) {
|
||||||
|
return 0.5 * Math.pow(1024, k - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.5 * (- Math.pow(2, - 10 * (k - 1)) + 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Circular: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
return 1 - Math.sqrt(1 - k * k);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
return Math.sqrt(1 - (--k * k));
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
if ((k *= 2) < 1) {
|
||||||
|
return - 0.5 * (Math.sqrt(1 - k * k) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Elastic: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
var s;
|
||||||
|
var a = 0.1;
|
||||||
|
var p = 0.4;
|
||||||
|
|
||||||
|
if (k === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k === 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a || a < 1) {
|
||||||
|
a = 1;
|
||||||
|
s = p / 4;
|
||||||
|
} else {
|
||||||
|
s = p * Math.asin(1 / a) / (2 * Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
return - (a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
var s;
|
||||||
|
var a = 0.1;
|
||||||
|
var p = 0.4;
|
||||||
|
|
||||||
|
if (k === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k === 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a || a < 1) {
|
||||||
|
a = 1;
|
||||||
|
s = p / 4;
|
||||||
|
} else {
|
||||||
|
s = p * Math.asin(1 / a) / (2 * Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (a * Math.pow(2, - 10 * k) * Math.sin((k - s) * (2 * Math.PI) / p) + 1);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
var s;
|
||||||
|
var a = 0.1;
|
||||||
|
var p = 0.4;
|
||||||
|
|
||||||
|
if (k === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k === 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a || a < 1) {
|
||||||
|
a = 1;
|
||||||
|
s = p / 4;
|
||||||
|
} else {
|
||||||
|
s = p * Math.asin(1 / a) / (2 * Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((k *= 2) < 1) {
|
||||||
|
return - 0.5 * (a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));
|
||||||
|
}
|
||||||
|
|
||||||
|
return a * Math.pow(2, -10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Back: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
var s = 1.70158;
|
||||||
|
|
||||||
|
return k * k * ((s + 1) * k - s);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
var s = 1.70158;
|
||||||
|
|
||||||
|
return --k * k * ((s + 1) * k + s) + 1;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
var s = 1.70158 * 1.525;
|
||||||
|
|
||||||
|
if ((k *= 2) < 1) {
|
||||||
|
return 0.5 * (k * k * ((s + 1) * k - s));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Bounce: {
|
||||||
|
|
||||||
|
In: function (k) {
|
||||||
|
|
||||||
|
return 1 - TWEEN.Easing.Bounce.Out(1 - k);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Out: function (k) {
|
||||||
|
|
||||||
|
if (k < (1 / 2.75)) {
|
||||||
|
return 7.5625 * k * k;
|
||||||
|
} else if (k < (2 / 2.75)) {
|
||||||
|
return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
|
||||||
|
} else if (k < (2.5 / 2.75)) {
|
||||||
|
return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
|
||||||
|
} else {
|
||||||
|
return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
InOut: function (k) {
|
||||||
|
|
||||||
|
if (k < 0.5) {
|
||||||
|
return TWEEN.Easing.Bounce.In(k * 2) * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TWEEN.Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
TWEEN.Interpolation = {
|
||||||
|
|
||||||
|
Linear: function (v, k) {
|
||||||
|
|
||||||
|
var m = v.length - 1;
|
||||||
|
var f = m * k;
|
||||||
|
var i = Math.floor(f);
|
||||||
|
var fn = TWEEN.Interpolation.Utils.Linear;
|
||||||
|
|
||||||
|
if (k < 0) {
|
||||||
|
return fn(v[0], v[1], f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k > 1) {
|
||||||
|
return fn(v[m], v[m - 1], m - f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(v[i], v[i + 1 > m ? m : i + 1], f - i);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Bezier: function (v, k) {
|
||||||
|
|
||||||
|
var b = 0;
|
||||||
|
var n = v.length - 1;
|
||||||
|
var pw = Math.pow;
|
||||||
|
var bn = TWEEN.Interpolation.Utils.Bernstein;
|
||||||
|
|
||||||
|
for (var i = 0; i <= n; i++) {
|
||||||
|
b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return b;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
CatmullRom: function (v, k) {
|
||||||
|
|
||||||
|
var m = v.length - 1;
|
||||||
|
var f = m * k;
|
||||||
|
var i = Math.floor(f);
|
||||||
|
var fn = TWEEN.Interpolation.Utils.CatmullRom;
|
||||||
|
|
||||||
|
if (v[0] === v[m]) {
|
||||||
|
|
||||||
|
if (k < 0) {
|
||||||
|
i = Math.floor(f = m * (1 + k));
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (k < 0) {
|
||||||
|
return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k > 1) {
|
||||||
|
return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Utils: {
|
||||||
|
|
||||||
|
Linear: function (p0, p1, t) {
|
||||||
|
|
||||||
|
return (p1 - p0) * t + p0;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Bernstein: function (n, i) {
|
||||||
|
|
||||||
|
var fc = TWEEN.Interpolation.Utils.Factorial;
|
||||||
|
|
||||||
|
return fc(n) / fc(i) / fc(n - i);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
Factorial: (function () {
|
||||||
|
|
||||||
|
var a = [1];
|
||||||
|
|
||||||
|
return function (n) {
|
||||||
|
|
||||||
|
var s = 1;
|
||||||
|
|
||||||
|
if (a[n]) {
|
||||||
|
return a[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = n; i > 1; i--) {
|
||||||
|
s *= i;
|
||||||
|
}
|
||||||
|
|
||||||
|
a[n] = s;
|
||||||
|
return s;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
})(),
|
||||||
|
|
||||||
|
CatmullRom: function (p0, p1, p2, p3, t) {
|
||||||
|
|
||||||
|
var v0 = (p2 - p0) * 0.5;
|
||||||
|
var v1 = (p3 - p1) * 0.5;
|
||||||
|
var t2 = t * t;
|
||||||
|
var t3 = t * t2;
|
||||||
|
|
||||||
|
return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (- 3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// UMD (Universal Module Definition)
|
||||||
|
(function (root) {
|
||||||
|
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
|
||||||
|
// AMD
|
||||||
|
define([], function () {
|
||||||
|
return TWEEN;
|
||||||
|
});
|
||||||
|
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
|
||||||
|
// Node.js
|
||||||
|
module.exports = TWEEN;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Global variable
|
||||||
|
root.TWEEN = TWEEN;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})(this);
|
||||||
|
|
||||||
|
loadTween = function(){
|
||||||
|
return TWEEN
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
// included here to ensure walkApi.js can be used as an API, separate from walk.js
|
// included here to ensure walkApi.js can be used as an API, separate from walk.js
|
||||||
Script.include("./libraries/walkConstants.js");
|
Script.include("walkConstants.js");
|
||||||
|
|
||||||
Avatar = function() {
|
Avatar = function() {
|
||||||
// if Hydras are connected, the only way to enable use is to never set any arm joint rotation
|
// if Hydras are connected, the only way to enable use is to never set any arm joint rotation
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
var WALK_SETTINGS_HTML_URL = Script.resolvePath('../html/walkSettings.html');
|
||||||
|
|
||||||
WalkSettings = function() {
|
WalkSettings = function() {
|
||||||
var _visible = false;
|
var _visible = false;
|
||||||
var _innerWidth = Window.innerWidth;
|
var _innerWidth = Window.innerWidth;
|
||||||
|
@ -69,7 +71,7 @@ WalkSettings = function() {
|
||||||
// web window
|
// web window
|
||||||
const PANEL_WIDTH = 200;
|
const PANEL_WIDTH = 200;
|
||||||
const PANEL_HEIGHT = 180;
|
const PANEL_HEIGHT = 180;
|
||||||
var _url = Script.resolvePath('html/walkSettings.html');
|
var _url = WALK_SETTINGS_HTML_URL;
|
||||||
var _webWindow = new WebWindow('Walk Settings', _url, PANEL_WIDTH, PANEL_HEIGHT, false);
|
var _webWindow = new WebWindow('Walk Settings', _url, PANEL_WIDTH, PANEL_HEIGHT, false);
|
||||||
_webWindow.setVisible(false);
|
_webWindow.setVisible(false);
|
||||||
_webWindow.eventBridge.webEventReceived.connect(function(data) {
|
_webWindow.eventBridge.webEventReceived.connect(function(data) {
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
// FIXME Script paths have to be relative to the caller, in this case libraries/OmniTool.js
|
// FIXME Script paths have to be relative to the caller, in this case libraries/OmniTool.js
|
||||||
Script.include("../magBalls/constants.js");
|
Script.include("magBalls/constants.js");
|
||||||
Script.include("../magBalls/graph.js");
|
Script.include("magBalls/graph.js");
|
||||||
Script.include("../magBalls/edgeSpring.js");
|
Script.include("magBalls/edgeSpring.js");
|
||||||
Script.include("../magBalls/magBalls.js");
|
Script.include("magBalls/magBalls.js");
|
||||||
Script.include("avatarRelativeOverlays.js");
|
Script.include("libraries/avatarRelativeOverlays.js");
|
||||||
|
|
||||||
OmniToolModuleType = "MagBallsController"
|
OmniToolModuleType = "MagBallsController"
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ MODE_INFO[BALL_EDIT_MODE_ADD] = {
|
||||||
},
|
},
|
||||||
colors: [ COLORS.GREEN, COLORS.BLUE ],
|
colors: [ COLORS.GREEN, COLORS.BLUE ],
|
||||||
// FIXME use an http path or find a way to get the relative path to the file
|
// FIXME use an http path or find a way to get the relative path to the file
|
||||||
url: Script.resolvePath('../html/magBalls/addMode.html'),
|
url: Script.resolvePath('html/magBalls/addMode.html'),
|
||||||
};
|
};
|
||||||
|
|
||||||
MODE_INFO[BALL_EDIT_MODE_DELETE] = {
|
MODE_INFO[BALL_EDIT_MODE_DELETE] = {
|
||||||
|
@ -45,7 +45,7 @@ MODE_INFO[BALL_EDIT_MODE_DELETE] = {
|
||||||
},
|
},
|
||||||
colors: [ COLORS.RED, COLORS.BLUE ],
|
colors: [ COLORS.RED, COLORS.BLUE ],
|
||||||
// FIXME use an http path or find a way to get the relative path to the file
|
// FIXME use an http path or find a way to get the relative path to the file
|
||||||
url: Script.resolvePath('../html/magBalls/deleteMode.html'),
|
url: Script.resolvePath('html/magBalls/deleteMode.html'),
|
||||||
};
|
};
|
||||||
|
|
||||||
var UI_POSITION_MODE_LABEL = Vec3.multiply(0.5,
|
var UI_POSITION_MODE_LABEL = Vec3.multiply(0.5,
|
||||||
|
|
1
examples/marketplace/S3Server/Procfile
Normal file
1
examples/marketplace/S3Server/Procfile
Normal file
|
@ -0,0 +1 @@
|
||||||
|
web: node index.js
|
57
examples/marketplace/S3Server/index.js
Normal file
57
examples/marketplace/S3Server/index.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
//
|
||||||
|
// index.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Eric Levin on 11/10/2015.
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is a simple REST API that allows an interface client script to get a list of files paths from an S3 bucket.
|
||||||
|
// To change your bucket, modify line 34 to your desired bucket.
|
||||||
|
// Please refer to http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-configuring.html
|
||||||
|
// for instructions on how to configure the SDK to work with your bucket.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
var express = require('express');
|
||||||
|
var app = express();
|
||||||
|
var AWS = require('aws-sdk');
|
||||||
|
var url = require('url');
|
||||||
|
var querystring = require('querystring');
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
|
AWS.config.update({
|
||||||
|
region: "us-east-1"
|
||||||
|
});
|
||||||
|
|
||||||
|
var s3 = new AWS.S3();
|
||||||
|
|
||||||
|
app.set('port', (process.env.PORT || 5000));
|
||||||
|
|
||||||
|
app.get('/', function(req, res) {
|
||||||
|
var urlParts = url.parse(req.url)
|
||||||
|
var query = querystring.parse(urlParts.query);
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
Bucket: "hifi-public",
|
||||||
|
Marker: query.assetDir,
|
||||||
|
MaxKeys: 10
|
||||||
|
};
|
||||||
|
s3.listObjects(params, function(err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err, err.stack);
|
||||||
|
res.send("ERROR")
|
||||||
|
} else {
|
||||||
|
var keys = _.pluck(data.Contents, 'Key')
|
||||||
|
res.send({
|
||||||
|
urls: keys
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.listen(app.get('port'), function() {
|
||||||
|
console.log('Node app is running on port', app.get('port'));
|
||||||
|
})
|
||||||
|
|
18
examples/marketplace/S3Server/package.json
Normal file
18
examples/marketplace/S3Server/package.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "s3fileserver",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "eric",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"aws-sdk": "^2.2.15",
|
||||||
|
"express": "^4.13.3",
|
||||||
|
"querystring": "^0.2.0",
|
||||||
|
"underscore": "^1.8.3",
|
||||||
|
"url": "^0.11.0"
|
||||||
|
}
|
||||||
|
}
|
95
examples/marketplace/dynamicLoader.js
Normal file
95
examples/marketplace/dynamicLoader.js
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
//
|
||||||
|
// dynamicLoader.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Eric Levin on 11/10/2015.
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This script is an example of a way to dynamically load and place models in a grid from a specified s3 directory on the hifi-public bucket.
|
||||||
|
// The directory can be specified by changing the query string variable on line 19 to the desired relative path.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var BASE_URL = "https://hifi-public.s3.amazonaws.com/";
|
||||||
|
var models = [];
|
||||||
|
var floorPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));;
|
||||||
|
floorPosition.y = MyAvatar.position.y - 5;
|
||||||
|
var floor = Entities.addEntity({
|
||||||
|
type: "Model",
|
||||||
|
modelURL: "https://hifi-public.s3.amazonaws.com/ozan/3d_marketplace/props/floor/3d_mp_floor.fbx",
|
||||||
|
position: floorPosition,
|
||||||
|
shapeType: 'box',
|
||||||
|
dimensions: {
|
||||||
|
x: 1000,
|
||||||
|
y: 9,
|
||||||
|
z: 1000
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var urls = [];
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.open("GET", "https://serene-headland-4300.herokuapp.com/?assetDir=ozan/3d_marketplace/sets", false);
|
||||||
|
req.send();
|
||||||
|
|
||||||
|
var res = req.responseText;
|
||||||
|
var urls = JSON.parse(res).urls;
|
||||||
|
if (urls.length > 0) {
|
||||||
|
// We've got an array of urls back from server- let's display them in grid
|
||||||
|
createGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createGrid() {
|
||||||
|
var fbxUrls = urls.filter(function(url) {
|
||||||
|
return url.indexOf('fbx') !== -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
var modelParams = {
|
||||||
|
type: "Model",
|
||||||
|
dimensions: {
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
z: 10
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var modelPosition = {
|
||||||
|
x: floorPosition.x + 10,
|
||||||
|
y: floorPosition.y + 8.5,
|
||||||
|
z: floorPosition.z
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < fbxUrls.length; i++) {
|
||||||
|
if(i % 2 === 0) {
|
||||||
|
modelPosition.x = floorPosition.x - 40
|
||||||
|
} else {
|
||||||
|
modelPosition.x = floorPosition.x + 40
|
||||||
|
}
|
||||||
|
modelPosition.z -= 30;
|
||||||
|
modelParams.position = modelPosition;
|
||||||
|
modelParams.modelURL = BASE_URL + fbxUrls[i]
|
||||||
|
var model = Entities.addEntity(modelParams);
|
||||||
|
models.push(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.setTimeout(function() {
|
||||||
|
//Until we add callbacks on model loaded, we need to set a timeout and hope model is loaded by the time
|
||||||
|
//we hit it in order to set model dimensions correctly
|
||||||
|
for(var i = 0; i < models.length; i++){
|
||||||
|
var modelDimensions = Entities.getEntityProperties(models[i], 'naturalDimensions').naturalDimensions;
|
||||||
|
Entities.editEntity(models[i], {dimensions: modelDimensions});
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
Entities.deleteEntity(floor);
|
||||||
|
models.forEach(function(model) {
|
||||||
|
Entities.deleteEntity(model);
|
||||||
|
});
|
||||||
|
Entities.deleteEntity(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
|
@ -11,7 +11,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
Script.include("libraries/utils.js");
|
Script.include("../libraries/utils.js");
|
||||||
|
|
||||||
|
|
||||||
var RIGHT_HAND = 1;
|
var RIGHT_HAND = 1;
|
||||||
|
|
|
@ -1,217 +0,0 @@
|
||||||
//
|
|
||||||
// originalPositionResetter.js
|
|
||||||
// toybox
|
|
||||||
//
|
|
||||||
// Created by James B. Pollack @imgntn 10/16/2015
|
|
||||||
// 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
|
|
||||||
//
|
|
||||||
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
|
||||||
|
|
||||||
var TARGET_MODEL_URL = HIFI_PUBLIC_BUCKET + "models/ping_pong_gun/target.fbx";
|
|
||||||
var TARGET_COLLISION_HULL_URL = HIFI_PUBLIC_BUCKET + "models/ping_pong_gun/target_collision_hull.obj";
|
|
||||||
var TARGET_DIMENSIONS = {
|
|
||||||
x: 0.06,
|
|
||||||
y: 0.42,
|
|
||||||
z: 0.42
|
|
||||||
};
|
|
||||||
var TARGET_ROTATION = Quat.fromPitchYawRollDegrees(0, -55.25, 0);
|
|
||||||
|
|
||||||
var targetsScriptURL = Script.resolvePath('../ping_pong_gun/wallTarget.js');
|
|
||||||
|
|
||||||
|
|
||||||
var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx";
|
|
||||||
|
|
||||||
var NUMBER_OF_BALLS = 4;
|
|
||||||
var BALL_DIAMETER = 0.30;
|
|
||||||
var RESET_DISTANCE = 1;
|
|
||||||
var MINIMUM_MOVE_LENGTH = 0.05;
|
|
||||||
|
|
||||||
var totalTime = 0;
|
|
||||||
var lastUpdate = 0;
|
|
||||||
var UPDATE_INTERVAL = 1 / 5; // 5fps
|
|
||||||
|
|
||||||
var Resetter = {
|
|
||||||
searchForEntitiesToResetToOriginalPosition: function(searchOrigin, objectName) {
|
|
||||||
var ids = Entities.findEntities(searchOrigin, 5);
|
|
||||||
var objects = [];
|
|
||||||
var i;
|
|
||||||
var entityID;
|
|
||||||
var name;
|
|
||||||
for (i = 0; i < ids.length; i++) {
|
|
||||||
entityID = ids[i];
|
|
||||||
name = Entities.getEntityProperties(entityID, "name").name;
|
|
||||||
if (name === objectName) {
|
|
||||||
//we found an object to reset
|
|
||||||
objects.push(entityID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return objects;
|
|
||||||
},
|
|
||||||
deleteObjects: function(objects) {
|
|
||||||
while (objects.length > 0) {
|
|
||||||
Entities.deleteEntity(objects.pop());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
createBasketBalls: function() {
|
|
||||||
var position = {
|
|
||||||
x: 542.86,
|
|
||||||
y: 494.84,
|
|
||||||
z: 475.06
|
|
||||||
};
|
|
||||||
var i;
|
|
||||||
var ballPosition;
|
|
||||||
var collidingBall;
|
|
||||||
for (i = 0; i < NUMBER_OF_BALLS; i++) {
|
|
||||||
ballPosition = {
|
|
||||||
x: position.x,
|
|
||||||
y: position.y + BALL_DIAMETER * 2,
|
|
||||||
z: position.z + (BALL_DIAMETER) - (BALL_DIAMETER * i)
|
|
||||||
};
|
|
||||||
|
|
||||||
collidingBall = Entities.addEntity({
|
|
||||||
type: "Model",
|
|
||||||
name: 'Hifi-Basketball',
|
|
||||||
shapeType: 'Sphere',
|
|
||||||
position: ballPosition,
|
|
||||||
dimensions: {
|
|
||||||
x: BALL_DIAMETER,
|
|
||||||
y: BALL_DIAMETER,
|
|
||||||
z: BALL_DIAMETER
|
|
||||||
},
|
|
||||||
restitution: 1.0,
|
|
||||||
linearDamping: 0.00001,
|
|
||||||
gravity: {
|
|
||||||
x: 0,
|
|
||||||
y: -9.8,
|
|
||||||
z: 0
|
|
||||||
},
|
|
||||||
collisionsWillMove: true,
|
|
||||||
ignoreForCollisions: false,
|
|
||||||
modelURL: basketballURL,
|
|
||||||
userData: JSON.stringify({
|
|
||||||
originalPositionKey: {
|
|
||||||
originalPosition: ballPosition
|
|
||||||
},
|
|
||||||
resetMe: {
|
|
||||||
resetMe: true
|
|
||||||
},
|
|
||||||
grabbable: {
|
|
||||||
invertSolidWhileHeld: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
testBallDistanceFromStart: function(balls) {
|
|
||||||
var resetCount = 0;
|
|
||||||
balls.forEach(function(ball, index) {
|
|
||||||
var properties = Entities.getEntityProperties(ball, ["position", "userData"]);
|
|
||||||
var currentPosition = properties.position;
|
|
||||||
var originalPosition = properties.userData.originalPositionKey.originalPosition;
|
|
||||||
var distance = Vec3.subtract(originalPosition, currentPosition);
|
|
||||||
var length = Vec3.length(distance);
|
|
||||||
if (length > RESET_DISTANCE) {
|
|
||||||
Script.setTimeout(function() {
|
|
||||||
var newPosition = Entities.getEntityProperties(ball, "position").position;
|
|
||||||
var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition));
|
|
||||||
if (moving < MINIMUM_MOVE_LENGTH) {
|
|
||||||
if (resetCount === balls.length) {
|
|
||||||
this.deleteObjects(balls);
|
|
||||||
this.createBasketBalls();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
testTargetDistanceFromStart: function(targets) {
|
|
||||||
targets.forEach(function(target, index) {
|
|
||||||
var properties = Entities.getEntityProperties(target, ["position", "userData"]);
|
|
||||||
var currentPosition = properties.position;
|
|
||||||
var originalPosition = properties.userData.originalPositionKey.originalPosition;
|
|
||||||
var distance = Vec3.subtract(originalPosition, currentPosition);
|
|
||||||
var length = Vec3.length(distance);
|
|
||||||
if (length > RESET_DISTANCE) {
|
|
||||||
Script.setTimeout(function() {
|
|
||||||
var newPosition = Entities.getEntityProperties(target, "position").position;
|
|
||||||
var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition));
|
|
||||||
if (moving < MINIMUM_MOVE_LENGTH) {
|
|
||||||
|
|
||||||
Entities.deleteEntity(target);
|
|
||||||
|
|
||||||
var targetProperties = {
|
|
||||||
name: 'Hifi-Target',
|
|
||||||
type: 'Model',
|
|
||||||
modelURL: TARGET_MODEL_URL,
|
|
||||||
shapeType: 'compound',
|
|
||||||
collisionsWillMove: true,
|
|
||||||
dimensions: TARGET_DIMENSIONS,
|
|
||||||
compoundShapeURL: TARGET_COLLISION_HULL_URL,
|
|
||||||
position: originalPosition,
|
|
||||||
rotation: TARGET_ROTATION,
|
|
||||||
script: targetsScriptURL,
|
|
||||||
userData: JSON.stringify({
|
|
||||||
resetMe: {
|
|
||||||
resetMe: true
|
|
||||||
},
|
|
||||||
grabbableKey: {
|
|
||||||
grabbable: false
|
|
||||||
},
|
|
||||||
originalPositionKey: originalPosition
|
|
||||||
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
Entities.addEntity(targetProperties);
|
|
||||||
}
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function update(deltaTime) {
|
|
||||||
|
|
||||||
if (!Entities.serversExist() || !Entities.canRez()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
totalTime += deltaTime;
|
|
||||||
|
|
||||||
// We don't want to edit the entity EVERY update cycle, because that's just a lot
|
|
||||||
// of wasted bandwidth and extra effort on the server for very little visual gain
|
|
||||||
if (totalTime - lastUpdate > UPDATE_INTERVAL) {
|
|
||||||
//do stuff
|
|
||||||
var balls = Resetter.searchForEntitiesToResetToOriginalPosition({
|
|
||||||
x: 542.86,
|
|
||||||
y: 494.84,
|
|
||||||
z: 475.06
|
|
||||||
}, "Hifi-Basketball");
|
|
||||||
|
|
||||||
var targets = Resetter.searchForEntitiesToResetToOriginalPosition({
|
|
||||||
x: 548.68,
|
|
||||||
y: 497.30,
|
|
||||||
z: 509.74
|
|
||||||
}, "Hifi-Target");
|
|
||||||
|
|
||||||
if (balls.length !== 0) {
|
|
||||||
Resetter.testBallDistanceFromStart(balls);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targets.length !== 0) {
|
|
||||||
Resetter.testTargetDistanceFromStart(targets);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastUpdate = totalTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Script.update.connect(update);
|
|
|
@ -10,7 +10,6 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||||
|
|
||||||
Script.include("../../utilities.js");
|
|
||||||
Script.include("../../libraries/utils.js");
|
Script.include("../../libraries/utils.js");
|
||||||
|
|
||||||
var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx';
|
var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx';
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
|
|
||||||
Script.include("../../utilities.js");
|
|
||||||
Script.include("../../libraries/utils.js");
|
Script.include("../../libraries/utils.js");
|
||||||
|
|
||||||
var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/models/bubblewand/bubble.fbx";
|
var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/models/bubblewand/bubble.fbx";
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
Script.include("../../utilities.js");
|
|
||||||
Script.include("../../libraries/utils.js");
|
Script.include("../../libraries/utils.js");
|
||||||
var _this;
|
var _this;
|
||||||
// this is the "constructor" for the entity as a JS object we don't do much here
|
// this is the "constructor" for the entity as a JS object we don't do much here
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||||
Script.include("../../utilities.js");
|
Script.include("../../libraries/utils.js");
|
||||||
|
|
||||||
var scriptURL = Script.resolvePath('pingPongGun.js');
|
var scriptURL = Script.resolvePath('pingPongGun.js');
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||||
Script.include("../../utilities.js");
|
|
||||||
Script.include("../../libraries/utils.js");
|
Script.include("../../libraries/utils.js");
|
||||||
var scriptURL = Script.resolvePath('wallTarget.js');
|
var scriptURL = Script.resolvePath('wallTarget.js');
|
||||||
|
|
||||||
|
|
|
@ -1132,14 +1132,6 @@ void MyAvatar::setJointRotations(QVector<glm::quat> jointRotations) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::setJointTranslations(QVector<glm::vec3> jointTranslations) {
|
|
||||||
int numStates = glm::min(_skeletonModel.getJointStateCount(), jointTranslations.size());
|
|
||||||
for (int i = 0; i < numStates; ++i) {
|
|
||||||
// HACK: ATM only Recorder calls setJointTranslations() so we hardcode its priority here
|
|
||||||
_skeletonModel.setJointTranslation(i, true, jointTranslations[i], RECORDER_PRIORITY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) {
|
void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation),
|
QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation),
|
||||||
|
|
|
@ -192,7 +192,6 @@ public:
|
||||||
void clearLookAtTargetAvatar();
|
void clearLookAtTargetAvatar();
|
||||||
|
|
||||||
virtual void setJointRotations(QVector<glm::quat> jointRotations) override;
|
virtual void setJointRotations(QVector<glm::quat> jointRotations) override;
|
||||||
virtual void setJointTranslations(QVector<glm::vec3> jointTranslations) override;
|
|
||||||
virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) override;
|
virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) override;
|
||||||
virtual void setJointRotation(int index, const glm::quat& rotation) override;
|
virtual void setJointRotation(int index, const glm::quat& rotation) override;
|
||||||
virtual void setJointTranslation(int index, const glm::vec3& translation) override;
|
virtual void setJointTranslation(int index, const glm::vec3& translation) override;
|
||||||
|
|
|
@ -247,23 +247,37 @@ void Player::play() {
|
||||||
_frameInterpolationFactor);
|
_frameInterpolationFactor);
|
||||||
_avatar->setTargetScale(context->scale * scale);
|
_avatar->setTargetScale(context->scale * scale);
|
||||||
|
|
||||||
|
// Joint array playback
|
||||||
|
// FIXME: THis is still using a deprecated path to assign the joint orientation since setting the full RawJointData array doesn't
|
||||||
|
// work for Avatar. We need to fix this working with the animation team
|
||||||
|
const auto& prevJointArray = currentFrame.getJointArray();
|
||||||
|
const auto& nextJointArray = currentFrame.getJointArray();
|
||||||
|
QVector<JointData> jointArray(prevJointArray.size());
|
||||||
|
QVector<glm::quat> jointRotations(prevJointArray.size()); // FIXME: remove once the setRawJointData is fixed
|
||||||
|
QVector<glm::vec3> jointTranslations(prevJointArray.size()); // FIXME: remove once the setRawJointData is fixed
|
||||||
|
|
||||||
QVector<glm::quat> jointRotations(currentFrame.getJointRotations().size());
|
for (int i = 0; i < jointArray.size(); i++) {
|
||||||
for (int i = 0; i < currentFrame.getJointRotations().size(); ++i) {
|
const auto& prevJoint = prevJointArray[i];
|
||||||
jointRotations[i] = safeMix(currentFrame.getJointRotations()[i],
|
const auto& nextJoint = nextJointArray[i];
|
||||||
nextFrame.getJointRotations()[i],
|
auto& joint = jointArray[i];
|
||||||
_frameInterpolationFactor);
|
|
||||||
|
// Rotation
|
||||||
|
joint.rotationSet = prevJoint.rotationSet || nextJoint.rotationSet;
|
||||||
|
if (joint.rotationSet) {
|
||||||
|
joint.rotation = safeMix(prevJoint.rotation, nextJoint.rotation, _frameInterpolationFactor);
|
||||||
|
jointRotations[i] = joint.rotation; // FIXME: remove once the setRawJointData is fixed
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<glm::vec3> jointTranslations(currentFrame.getJointTranslations().size());
|
joint.translationSet = prevJoint.translationSet || nextJoint.translationSet;
|
||||||
for (int i = 0; i < currentFrame.getJointTranslations().size(); ++i) {
|
if (joint.translationSet) {
|
||||||
jointTranslations[i] =
|
joint.translation = glm::mix(prevJoint.translation, nextJoint.translation, _frameInterpolationFactor);
|
||||||
currentFrame.getJointTranslations()[i] * (1.0f - _frameInterpolationFactor) +
|
jointTranslations[i] = joint.translation; // FIXME: remove once the setRawJointData is fixed
|
||||||
nextFrame.getJointTranslations()[i] * _frameInterpolationFactor;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_avatar->setJointRotations(jointRotations);
|
// _avatar->setRawJointData(jointArray); // FIXME: Enable once the setRawJointData is fixed
|
||||||
_avatar->setJointTranslations(jointTranslations);
|
_avatar->setJointRotations(jointRotations); // FIXME: remove once the setRawJointData is fixed
|
||||||
|
// _avatar->setJointTranslations(jointTranslations); // FIXME: remove once the setRawJointData is fixed
|
||||||
|
|
||||||
HeadData* head = const_cast<HeadData*>(_avatar->getHeadData());
|
HeadData* head = const_cast<HeadData*>(_avatar->getHeadData());
|
||||||
if (head) {
|
if (head) {
|
||||||
|
@ -423,3 +437,4 @@ bool Player::computeCurrentFrame() {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,12 +100,15 @@ void Recorder::record() {
|
||||||
const RecordingContext& context = _recording->getContext();
|
const RecordingContext& context = _recording->getContext();
|
||||||
RecordingFrame frame;
|
RecordingFrame frame;
|
||||||
frame.setBlendshapeCoefficients(_avatar->getHeadData()->getBlendshapeCoefficients());
|
frame.setBlendshapeCoefficients(_avatar->getHeadData()->getBlendshapeCoefficients());
|
||||||
frame.setJointRotations(_avatar->getJointRotations());
|
|
||||||
|
// Capture the full skeleton joint data
|
||||||
|
auto& jointData = _avatar->getRawJointData();
|
||||||
|
frame.setJointArray(jointData);
|
||||||
|
|
||||||
frame.setTranslation(context.orientationInv * (_avatar->getPosition() - context.position));
|
frame.setTranslation(context.orientationInv * (_avatar->getPosition() - context.position));
|
||||||
frame.setRotation(context.orientationInv * _avatar->getOrientation());
|
frame.setRotation(context.orientationInv * _avatar->getOrientation());
|
||||||
frame.setScale(_avatar->getTargetScale() / context.scale);
|
frame.setScale(_avatar->getTargetScale() / context.scale);
|
||||||
|
|
||||||
|
|
||||||
const HeadData* head = _avatar->getHeadData();
|
const HeadData* head = _avatar->getHeadData();
|
||||||
if (head) {
|
if (head) {
|
||||||
glm::vec3 rotationDegrees = glm::vec3(head->getFinalPitch(),
|
glm::vec3 rotationDegrees = glm::vec3(head->getFinalPitch(),
|
||||||
|
@ -123,7 +126,7 @@ void Recorder::record() {
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
qCDebug(avatars) << "Recording frame #" << _recording->getFrameNumber();
|
qCDebug(avatars) << "Recording frame #" << _recording->getFrameNumber();
|
||||||
qCDebug(avatars) << "Blendshapes:" << frame.getBlendshapeCoefficients().size();
|
qCDebug(avatars) << "Blendshapes:" << frame.getBlendshapeCoefficients().size();
|
||||||
qCDebug(avatars) << "JointRotations:" << frame.getJointRotations().size();
|
qCDebug(avatars) << "JointArray:" << frame.getJointArray().size();
|
||||||
qCDebug(avatars) << "Translation:" << frame.getTranslation();
|
qCDebug(avatars) << "Translation:" << frame.getTranslation();
|
||||||
qCDebug(avatars) << "Rotation:" << frame.getRotation();
|
qCDebug(avatars) << "Rotation:" << frame.getRotation();
|
||||||
qCDebug(avatars) << "Scale:" << frame.getScale();
|
qCDebug(avatars) << "Scale:" << frame.getScale();
|
||||||
|
|
|
@ -229,17 +229,22 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) {
|
||||||
++maskIndex;
|
++maskIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Joint Rotations
|
const auto& jointArray = frame.getJointArray();
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
numJoints = frame.getJointRotations().size();
|
numJoints = jointArray.size();
|
||||||
stream << numJoints;
|
stream << numJoints;
|
||||||
mask.resize(mask.size() + numJoints);
|
// 2 fields per joints
|
||||||
|
mask.resize(mask.size() + numJoints * 2);
|
||||||
}
|
}
|
||||||
for (quint32 j = 0; j < numJoints; ++j) {
|
for (quint32 j = 0; j < numJoints; j++) {
|
||||||
if (i == 0 ||
|
const auto& joint = jointArray[j];
|
||||||
frame._jointRotations[j] != previousFrame._jointRotations[j]) {
|
if (true) { //(joint.rotationSet) {
|
||||||
writeQuat(stream, frame._jointRotations[j]);
|
writeQuat(stream, joint.rotation);
|
||||||
// TODO -- handle translations
|
mask.setBit(maskIndex);
|
||||||
|
}
|
||||||
|
maskIndex++;
|
||||||
|
if (joint.translationSet) {
|
||||||
|
writeVec3(stream, joint.translation);
|
||||||
mask.setBit(maskIndex);
|
mask.setBit(maskIndex);
|
||||||
}
|
}
|
||||||
maskIndex++;
|
maskIndex++;
|
||||||
|
@ -408,11 +413,7 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filename.endsWith(".rec") || filename.endsWith(".REC")) {
|
if (!filename.endsWith(".hfr") && !filename.endsWith(".HFR")) {
|
||||||
qCDebug(avatars) << "Old .rec format";
|
|
||||||
readRecordingFromRecFile(recording, filename, byteArray);
|
|
||||||
return recording;
|
|
||||||
} else if (!filename.endsWith(".hfr") && !filename.endsWith(".HFR")) {
|
|
||||||
qCDebug(avatars) << "File extension not recognized";
|
qCDebug(avatars) << "File extension not recognized";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,18 +553,27 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString
|
||||||
stream >> frame._blendshapeCoefficients[j];
|
stream >> frame._blendshapeCoefficients[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Joint Rotations
|
// Joint Array
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
stream >> numJoints;
|
stream >> numJoints;
|
||||||
}
|
}
|
||||||
frame._jointRotations.resize(numJoints);
|
|
||||||
|
frame._jointArray.resize(numJoints);
|
||||||
for (quint32 j = 0; j < numJoints; ++j) {
|
for (quint32 j = 0; j < numJoints; ++j) {
|
||||||
if (!mask[maskIndex++] || !readQuat(stream, frame._jointRotations[j])) {
|
auto& joint = frame._jointArray[2];
|
||||||
frame._jointRotations[j] = previousFrame._jointRotations[j];
|
|
||||||
}
|
if (mask[maskIndex++] && readQuat(stream, joint.rotation)) {
|
||||||
|
joint.rotationSet = true;
|
||||||
|
} else {
|
||||||
|
joint.rotationSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO -- handle translations
|
if (mask[maskIndex++] || readVec3(stream, joint.translation)) {
|
||||||
|
joint.translationSet = true;
|
||||||
|
} else {
|
||||||
|
joint.translationSet = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!mask[maskIndex++] || !readVec3(stream, frame._translation)) {
|
if (!mask[maskIndex++] || !readVec3(stream, frame._translation)) {
|
||||||
frame._translation = previousFrame._translation;
|
frame._translation = previousFrame._translation;
|
||||||
|
@ -649,167 +659,3 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString
|
||||||
return recording;
|
return recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QString& filename, const QByteArray& byteArray) {
|
|
||||||
QElapsedTimer timer;
|
|
||||||
timer.start();
|
|
||||||
|
|
||||||
if (!recording) {
|
|
||||||
recording = QSharedPointer<Recording>::create();
|
|
||||||
}
|
|
||||||
|
|
||||||
QDataStream fileStream(byteArray);
|
|
||||||
|
|
||||||
fileStream >> recording->_timestamps;
|
|
||||||
RecordingFrame baseFrame;
|
|
||||||
|
|
||||||
// Blendshape coefficients
|
|
||||||
fileStream >> baseFrame._blendshapeCoefficients;
|
|
||||||
|
|
||||||
// Joint Rotations
|
|
||||||
int jointRotationSize;
|
|
||||||
fileStream >> jointRotationSize;
|
|
||||||
baseFrame._jointRotations.resize(jointRotationSize);
|
|
||||||
for (int i = 0; i < jointRotationSize; ++i) {
|
|
||||||
fileStream >> baseFrame._jointRotations[i].x >> baseFrame._jointRotations[i].y >> baseFrame._jointRotations[i].z >> baseFrame._jointRotations[i].w;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO -- handle translations
|
|
||||||
|
|
||||||
fileStream >> baseFrame._translation.x >> baseFrame._translation.y >> baseFrame._translation.z;
|
|
||||||
fileStream >> baseFrame._rotation.x >> baseFrame._rotation.y >> baseFrame._rotation.z >> baseFrame._rotation.w;
|
|
||||||
fileStream >> baseFrame._scale;
|
|
||||||
fileStream >> baseFrame._headRotation.x >> baseFrame._headRotation.y >> baseFrame._headRotation.z >> baseFrame._headRotation.w;
|
|
||||||
fileStream >> baseFrame._leanSideways;
|
|
||||||
fileStream >> baseFrame._leanForward;
|
|
||||||
|
|
||||||
|
|
||||||
// Fake context
|
|
||||||
RecordingContext& context = recording->getContext();
|
|
||||||
context.globalTimestamp = usecTimestampNow();
|
|
||||||
context.domain = DependencyManager::get<NodeList>()->getDomainHandler().getHostname();
|
|
||||||
context.position = glm::vec3(144.5f, 3.3f, 181.3f);
|
|
||||||
context.orientation = glm::angleAxis(glm::radians(-92.5f), glm::vec3(0, 1, 0));;
|
|
||||||
context.scale = baseFrame._scale;
|
|
||||||
context.headModel = "http://public.highfidelity.io/models/heads/Emily_v4.fst";
|
|
||||||
context.skeletonModel = "http://public.highfidelity.io/models/skeletons/EmilyCutMesh_A.fst";
|
|
||||||
context.displayName = "Leslie";
|
|
||||||
context.attachments.clear();
|
|
||||||
AttachmentData data;
|
|
||||||
data.modelURL = "http://public.highfidelity.io/models/attachments/fbx.fst";
|
|
||||||
data.jointName = "RightHand" ;
|
|
||||||
data.translation = glm::vec3(0.04f, 0.07f, 0.0f);
|
|
||||||
data.rotation = glm::angleAxis(glm::radians(102.0f), glm::vec3(0, 1, 0));
|
|
||||||
data.scale = 0.20f;
|
|
||||||
context.attachments << data;
|
|
||||||
|
|
||||||
context.orientationInv = glm::inverse(context.orientation);
|
|
||||||
|
|
||||||
baseFrame._translation = glm::vec3();
|
|
||||||
baseFrame._rotation = glm::quat();
|
|
||||||
baseFrame._scale = 1.0f;
|
|
||||||
|
|
||||||
recording->_frames << baseFrame;
|
|
||||||
|
|
||||||
for (int i = 1; i < recording->_timestamps.size(); ++i) {
|
|
||||||
QBitArray mask;
|
|
||||||
QByteArray buffer;
|
|
||||||
QDataStream stream(&buffer, QIODevice::ReadOnly);
|
|
||||||
RecordingFrame frame;
|
|
||||||
RecordingFrame& previousFrame = recording->_frames.last();
|
|
||||||
|
|
||||||
fileStream >> mask;
|
|
||||||
fileStream >> buffer;
|
|
||||||
int maskIndex = 0;
|
|
||||||
|
|
||||||
// Blendshape Coefficients
|
|
||||||
frame._blendshapeCoefficients.resize(baseFrame._blendshapeCoefficients.size());
|
|
||||||
for (int i = 0; i < baseFrame._blendshapeCoefficients.size(); ++i) {
|
|
||||||
if (mask[maskIndex++]) {
|
|
||||||
stream >> frame._blendshapeCoefficients[i];
|
|
||||||
} else {
|
|
||||||
frame._blendshapeCoefficients[i] = previousFrame._blendshapeCoefficients[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Joint Rotations
|
|
||||||
frame._jointRotations.resize(baseFrame._jointRotations.size());
|
|
||||||
for (int i = 0; i < baseFrame._jointRotations.size(); ++i) {
|
|
||||||
if (mask[maskIndex++]) {
|
|
||||||
stream >> frame._jointRotations[i].x >> frame._jointRotations[i].y >> frame._jointRotations[i].z >> frame._jointRotations[i].w;
|
|
||||||
} else {
|
|
||||||
frame._jointRotations[i] = previousFrame._jointRotations[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO -- handle translations
|
|
||||||
|
|
||||||
if (mask[maskIndex++]) {
|
|
||||||
stream >> frame._translation.x >> frame._translation.y >> frame._translation.z;
|
|
||||||
frame._translation = context.orientationInv * frame._translation;
|
|
||||||
} else {
|
|
||||||
frame._translation = previousFrame._translation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask[maskIndex++]) {
|
|
||||||
stream >> frame._rotation.x >> frame._rotation.y >> frame._rotation.z >> frame._rotation.w;
|
|
||||||
} else {
|
|
||||||
frame._rotation = previousFrame._rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask[maskIndex++]) {
|
|
||||||
stream >> frame._scale;
|
|
||||||
} else {
|
|
||||||
frame._scale = previousFrame._scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask[maskIndex++]) {
|
|
||||||
stream >> frame._headRotation.x >> frame._headRotation.y >> frame._headRotation.z >> frame._headRotation.w;
|
|
||||||
} else {
|
|
||||||
frame._headRotation = previousFrame._headRotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask[maskIndex++]) {
|
|
||||||
stream >> frame._leanSideways;
|
|
||||||
} else {
|
|
||||||
frame._leanSideways = previousFrame._leanSideways;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask[maskIndex++]) {
|
|
||||||
stream >> frame._leanForward;
|
|
||||||
} else {
|
|
||||||
frame._leanForward = previousFrame._leanForward;
|
|
||||||
}
|
|
||||||
|
|
||||||
recording->_frames << frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray audioArray;
|
|
||||||
fileStream >> audioArray;
|
|
||||||
|
|
||||||
// Cut down audio if necessary
|
|
||||||
int SAMPLE_SIZE = 2; // 16 bits
|
|
||||||
int MSEC_PER_SEC = 1000;
|
|
||||||
int audioLength = recording->getLength() * SAMPLE_SIZE * (AudioConstants::SAMPLE_RATE / MSEC_PER_SEC);
|
|
||||||
audioArray.chop(audioArray.size() - audioLength);
|
|
||||||
|
|
||||||
recording->addAudioPacket(audioArray);
|
|
||||||
|
|
||||||
qCDebug(avatars) << "Read " << byteArray.size() << " bytes in " << timer.elapsed() << " ms.";
|
|
||||||
|
|
||||||
// Set new filename
|
|
||||||
QString newFilename = filename;
|
|
||||||
if (newFilename.startsWith("http") || newFilename.startsWith("https") || newFilename.startsWith("ftp")) {
|
|
||||||
newFilename = QUrl(newFilename).fileName();
|
|
||||||
}
|
|
||||||
if (newFilename.endsWith(".rec") || newFilename.endsWith(".REC")) {
|
|
||||||
newFilename.chop(qstrlen(".rec"));
|
|
||||||
}
|
|
||||||
newFilename.append(".hfr");
|
|
||||||
newFilename = QFileInfo(newFilename).absoluteFilePath();
|
|
||||||
|
|
||||||
// Set recording to new format
|
|
||||||
writeRecordingToFile(recording, newFilename);
|
|
||||||
qCDebug(avatars) << "Recording has been successfully converted at" << newFilename;
|
|
||||||
return recording;
|
|
||||||
}
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ class AttachmentData;
|
||||||
class Recording;
|
class Recording;
|
||||||
class RecordingFrame;
|
class RecordingFrame;
|
||||||
class Sound;
|
class Sound;
|
||||||
|
class JointData;
|
||||||
|
|
||||||
typedef QSharedPointer<Recording> RecordingPointer;
|
typedef QSharedPointer<Recording> RecordingPointer;
|
||||||
|
|
||||||
|
@ -82,8 +83,7 @@ private:
|
||||||
class RecordingFrame {
|
class RecordingFrame {
|
||||||
public:
|
public:
|
||||||
QVector<float> getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
QVector<float> getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||||
QVector<glm::quat> getJointRotations() const { return _jointRotations; }
|
QVector<JointData> getJointArray() const { return _jointArray; }
|
||||||
QVector<glm::vec3> getJointTranslations() const { return _jointTranslations; }
|
|
||||||
glm::vec3 getTranslation() const { return _translation; }
|
glm::vec3 getTranslation() const { return _translation; }
|
||||||
glm::quat getRotation() const { return _rotation; }
|
glm::quat getRotation() const { return _rotation; }
|
||||||
float getScale() const { return _scale; }
|
float getScale() const { return _scale; }
|
||||||
|
@ -94,8 +94,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setBlendshapeCoefficients(QVector<float> blendshapeCoefficients);
|
void setBlendshapeCoefficients(QVector<float> blendshapeCoefficients);
|
||||||
void setJointRotations(QVector<glm::quat> jointRotations) { _jointRotations = jointRotations; }
|
void setJointArray(const QVector<JointData>& jointArray) { _jointArray = jointArray; }
|
||||||
void setJointTranslations(QVector<glm::vec3> jointTranslations) { _jointTranslations = jointTranslations; }
|
|
||||||
void setTranslation(const glm::vec3& translation) { _translation = translation; }
|
void setTranslation(const glm::vec3& translation) { _translation = translation; }
|
||||||
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
||||||
void setScale(float scale) { _scale = scale; }
|
void setScale(float scale) { _scale = scale; }
|
||||||
|
@ -106,8 +105,8 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<float> _blendshapeCoefficients;
|
QVector<float> _blendshapeCoefficients;
|
||||||
QVector<glm::quat> _jointRotations;
|
QVector<JointData> _jointArray;
|
||||||
QVector<glm::vec3> _jointTranslations;
|
|
||||||
glm::vec3 _translation;
|
glm::vec3 _translation;
|
||||||
glm::quat _rotation;
|
glm::quat _rotation;
|
||||||
float _scale;
|
float _scale;
|
||||||
|
@ -125,6 +124,5 @@ private:
|
||||||
|
|
||||||
void writeRecordingToFile(RecordingPointer recording, const QString& filename);
|
void writeRecordingToFile(RecordingPointer recording, const QString& filename);
|
||||||
RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString& filename);
|
RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString& filename);
|
||||||
RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QString& filename, const QByteArray& byteArray);
|
|
||||||
|
|
||||||
#endif // hifi_Recording_h
|
#endif // hifi_Recording_h
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "RecurseOctreeToMapOperator.h"
|
#include "RecurseOctreeToMapOperator.h"
|
||||||
#include "LogHandler.h"
|
#include "LogHandler.h"
|
||||||
|
|
||||||
|
static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50;
|
||||||
|
|
||||||
EntityTree::EntityTree(bool shouldReaverage) :
|
EntityTree::EntityTree(bool shouldReaverage) :
|
||||||
Octree(shouldReaverage),
|
Octree(shouldReaverage),
|
||||||
|
@ -384,16 +385,15 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool i
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) {
|
void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) {
|
||||||
|
quint64 deletedAt = usecTimestampNow();
|
||||||
const RemovedEntities& entities = theOperator.getEntities();
|
const RemovedEntities& entities = theOperator.getEntities();
|
||||||
foreach(const EntityToDeleteDetails& details, entities) {
|
foreach(const EntityToDeleteDetails& details, entities) {
|
||||||
EntityItemPointer theEntity = details.entity;
|
EntityItemPointer theEntity = details.entity;
|
||||||
|
|
||||||
if (getIsServer()) {
|
if (getIsServer()) {
|
||||||
// set up the deleted entities ID
|
// set up the deleted entities ID
|
||||||
quint64 deletedAt = usecTimestampNow();
|
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
|
||||||
_recentlyDeletedEntitiesLock.lockForWrite();
|
|
||||||
_recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID());
|
_recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID());
|
||||||
_recentlyDeletedEntitiesLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_simulation) {
|
if (_simulation) {
|
||||||
|
@ -802,25 +802,37 @@ void EntityTree::update() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) {
|
bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) {
|
||||||
|
quint64 considerEntitiesSince = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER;
|
||||||
|
|
||||||
// we can probably leverage the ordered nature of QMultiMap to do this quickly...
|
// we can probably leverage the ordered nature of QMultiMap to do this quickly...
|
||||||
bool hasSomethingNewer = false;
|
bool hasSomethingNewer = false;
|
||||||
|
|
||||||
_recentlyDeletedEntitiesLock.lockForRead();
|
QReadLocker locker(&_recentlyDeletedEntitiesLock);
|
||||||
QMultiMap<quint64, QUuid>::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin();
|
QMultiMap<quint64, QUuid>::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin();
|
||||||
while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) {
|
while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) {
|
||||||
if (iterator.key() > sinceTime) {
|
if (iterator.key() > considerEntitiesSince) {
|
||||||
hasSomethingNewer = true;
|
hasSomethingNewer = true;
|
||||||
|
break; // if we have at least one item, we don't need to keep searching
|
||||||
}
|
}
|
||||||
++iterator;
|
++iterator;
|
||||||
}
|
}
|
||||||
_recentlyDeletedEntitiesLock.unlock();
|
|
||||||
|
#ifdef EXTRA_ERASE_DEBUGGING
|
||||||
|
if (hasSomethingNewer) {
|
||||||
|
int elapsed = usecTimestampNow() - considerEntitiesSince;
|
||||||
|
int difference = considerEntitiesSince - sinceTime;
|
||||||
|
qDebug() << "EntityTree::hasEntitiesDeletedSince() sinceTime:" << sinceTime
|
||||||
|
<< "considerEntitiesSince:" << considerEntitiesSince << "elapsed:" << elapsed << "difference:" << difference;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return hasSomethingNewer;
|
return hasSomethingNewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sinceTime is an in/out parameter - it will be side effected with the last time sent out
|
// sinceTime is an in/out parameter - it will be side effected with the last time sent out
|
||||||
std::unique_ptr<NLPacket> EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime,
|
std::unique_ptr<NLPacket> EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime,
|
||||||
bool& hasMore) {
|
bool& hasMore) {
|
||||||
|
quint64 considerEntitiesSince = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER;
|
||||||
auto deletesPacket = NLPacket::create(PacketType::EntityErase);
|
auto deletesPacket = NLPacket::create(PacketType::EntityErase);
|
||||||
|
|
||||||
// pack in flags
|
// pack in flags
|
||||||
|
@ -841,7 +853,8 @@ std::unique_ptr<NLPacket> EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S
|
||||||
|
|
||||||
// we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been
|
// we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been
|
||||||
// deleted since we last sent to this node
|
// deleted since we last sent to this node
|
||||||
_recentlyDeletedEntitiesLock.lockForRead();
|
{
|
||||||
|
QReadLocker locker(&_recentlyDeletedEntitiesLock);
|
||||||
|
|
||||||
bool hasFilledPacket = false;
|
bool hasFilledPacket = false;
|
||||||
|
|
||||||
|
@ -851,12 +864,21 @@ std::unique_ptr<NLPacket> EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S
|
||||||
for (int valueItem = 0; valueItem < values.size(); ++valueItem) {
|
for (int valueItem = 0; valueItem < values.size(); ++valueItem) {
|
||||||
|
|
||||||
// if the timestamp is more recent then out last sent time, include it
|
// if the timestamp is more recent then out last sent time, include it
|
||||||
if (it.key() > sinceTime) {
|
if (it.key() > considerEntitiesSince) {
|
||||||
QUuid entityID = values.at(valueItem);
|
QUuid entityID = values.at(valueItem);
|
||||||
deletesPacket->write(entityID.toRfc4122());
|
|
||||||
|
|
||||||
|
// FIXME - we still seem to see cases where incorrect EntityIDs get sent from the server
|
||||||
|
// to the client. These were causing "lost" entities like flashlights and laser pointers
|
||||||
|
// now that we keep around some additional history of the erased entities and resend that
|
||||||
|
// history for a longer time window, these entities are not "lost". But we haven't yet
|
||||||
|
// found/fixed the underlying issue that caused bad UUIDs to be sent to some users.
|
||||||
|
deletesPacket->write(entityID.toRfc4122());
|
||||||
++numberOfIDs;
|
++numberOfIDs;
|
||||||
|
|
||||||
|
#ifdef EXTRA_ERASE_DEBUGGING
|
||||||
|
qDebug() << "EntityTree::encodeEntitiesDeletedSince() including:" << entityID;
|
||||||
|
#endif
|
||||||
|
|
||||||
// check to make sure we have room for one more ID
|
// check to make sure we have room for one more ID
|
||||||
if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) {
|
if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) {
|
||||||
hasFilledPacket = true;
|
hasFilledPacket = true;
|
||||||
|
@ -869,7 +891,6 @@ std::unique_ptr<NLPacket> EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S
|
||||||
if (hasFilledPacket) {
|
if (hasFilledPacket) {
|
||||||
// let our caller know how far we got
|
// let our caller know how far we got
|
||||||
sinceTime = it.key();
|
sinceTime = it.key();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -880,8 +901,7 @@ std::unique_ptr<NLPacket> EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S
|
||||||
if (it == _recentlyDeletedEntityItemIDs.constEnd()) {
|
if (it == _recentlyDeletedEntityItemIDs.constEnd()) {
|
||||||
hasMore = false;
|
hasMore = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
_recentlyDeletedEntitiesLock.unlock();
|
|
||||||
|
|
||||||
// replace the count for the number of included IDs
|
// replace the count for the number of included IDs
|
||||||
deletesPacket->seek(numberOfIDsPos);
|
deletesPacket->seek(numberOfIDsPos);
|
||||||
|
@ -893,14 +913,14 @@ std::unique_ptr<NLPacket> EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S
|
||||||
|
|
||||||
// called by the server when it knows all nodes have been sent deleted packets
|
// called by the server when it knows all nodes have been sent deleted packets
|
||||||
void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) {
|
void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) {
|
||||||
|
quint64 considerSinceTime = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER;
|
||||||
QSet<quint64> keysToRemove;
|
QSet<quint64> keysToRemove;
|
||||||
|
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
|
||||||
_recentlyDeletedEntitiesLock.lockForWrite();
|
|
||||||
QMultiMap<quint64, QUuid>::iterator iterator = _recentlyDeletedEntityItemIDs.begin();
|
QMultiMap<quint64, QUuid>::iterator iterator = _recentlyDeletedEntityItemIDs.begin();
|
||||||
|
|
||||||
// First find all the keys in the map that are older and need to be deleted
|
// First find all the keys in the map that are older and need to be deleted
|
||||||
while (iterator != _recentlyDeletedEntityItemIDs.end()) {
|
while (iterator != _recentlyDeletedEntityItemIDs.end()) {
|
||||||
if (iterator.key() <= sinceTime) {
|
if (iterator.key() <= considerSinceTime) {
|
||||||
keysToRemove << iterator.key();
|
keysToRemove << iterator.key();
|
||||||
}
|
}
|
||||||
++iterator;
|
++iterator;
|
||||||
|
@ -910,13 +930,14 @@ void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) {
|
||||||
foreach (quint64 value, keysToRemove) {
|
foreach (quint64 value, keysToRemove) {
|
||||||
_recentlyDeletedEntityItemIDs.remove(value);
|
_recentlyDeletedEntityItemIDs.remove(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
_recentlyDeletedEntitiesLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: consider consolidating processEraseMessageDetails() and processEraseMessage()
|
// TODO: consider consolidating processEraseMessageDetails() and processEraseMessage()
|
||||||
int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode) {
|
int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode) {
|
||||||
|
#ifdef EXTRA_ERASE_DEBUGGING
|
||||||
|
qDebug() << "EntityTree::processEraseMessage()";
|
||||||
|
#endif
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
packet.seek(sizeof(OCTREE_PACKET_FLAGS) + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME));
|
packet.seek(sizeof(OCTREE_PACKET_FLAGS) + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME));
|
||||||
|
|
||||||
|
@ -934,6 +955,9 @@ int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& s
|
||||||
}
|
}
|
||||||
|
|
||||||
QUuid entityID = QUuid::fromRfc4122(packet.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
QUuid entityID = QUuid::fromRfc4122(packet.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||||
|
#ifdef EXTRA_ERASE_DEBUGGING
|
||||||
|
qDebug() << " ---- EntityTree::processEraseMessage() contained ID:" << entityID;
|
||||||
|
#endif
|
||||||
|
|
||||||
EntityItemID entityItemID(entityID);
|
EntityItemID entityItemID(entityID);
|
||||||
entityItemIDsToDelete << entityItemID;
|
entityItemIDsToDelete << entityItemID;
|
||||||
|
@ -953,6 +977,9 @@ int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& s
|
||||||
// NOTE: Caller must lock the tree before calling this.
|
// NOTE: Caller must lock the tree before calling this.
|
||||||
// TODO: consider consolidating processEraseMessageDetails() and processEraseMessage()
|
// TODO: consider consolidating processEraseMessageDetails() and processEraseMessage()
|
||||||
int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {
|
int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) {
|
||||||
|
#ifdef EXTRA_ERASE_DEBUGGING
|
||||||
|
qDebug() << "EntityTree::processEraseMessageDetails()";
|
||||||
|
#endif
|
||||||
const unsigned char* packetData = (const unsigned char*)dataByteArray.constData();
|
const unsigned char* packetData = (const unsigned char*)dataByteArray.constData();
|
||||||
const unsigned char* dataAt = packetData;
|
const unsigned char* dataAt = packetData;
|
||||||
size_t packetLength = dataByteArray.size();
|
size_t packetLength = dataByteArray.size();
|
||||||
|
@ -979,6 +1006,10 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
|
||||||
dataAt += encodedID.size();
|
dataAt += encodedID.size();
|
||||||
processedBytes += encodedID.size();
|
processedBytes += encodedID.size();
|
||||||
|
|
||||||
|
#ifdef EXTRA_ERASE_DEBUGGING
|
||||||
|
qDebug() << " ---- EntityTree::processEraseMessageDetails() contains id:" << entityID;
|
||||||
|
#endif
|
||||||
|
|
||||||
EntityItemID entityItemID(entityID);
|
EntityItemID entityItemID(entityID);
|
||||||
entityItemIDsToDelete << entityItemID;
|
entityItemIDsToDelete << entityItemID;
|
||||||
|
|
||||||
|
|
|
@ -902,14 +902,19 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
||||||
BatchLoader* loader = new BatchLoader(urls);
|
BatchLoader* loader = new BatchLoader(urls);
|
||||||
|
|
||||||
auto evaluateScripts = [=](const QMap<QUrl, QString>& data) {
|
auto evaluateScripts = [=](const QMap<QUrl, QString>& data) {
|
||||||
|
auto parentURL = _parentURL;
|
||||||
for (QUrl url : urls) {
|
for (QUrl url : urls) {
|
||||||
QString contents = data[url];
|
QString contents = data[url];
|
||||||
if (contents.isNull()) {
|
if (contents.isNull()) {
|
||||||
qCDebug(scriptengine) << "Error loading file: " << url << "line:" << __LINE__;
|
qCDebug(scriptengine) << "Error loading file: " << url << "line:" << __LINE__;
|
||||||
} else {
|
} else {
|
||||||
|
// Set the parent url so that path resolution will be relative
|
||||||
|
// to this script's url during its initial evaluation
|
||||||
|
_parentURL = url.toString();
|
||||||
QScriptValue result = evaluate(contents, url.toString());
|
QScriptValue result = evaluate(contents, url.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_parentURL = parentURL;
|
||||||
|
|
||||||
if (callback.isFunction()) {
|
if (callback.isFunction()) {
|
||||||
QScriptValue(callback).call();
|
QScriptValue(callback).call();
|
||||||
|
|
Loading…
Reference in a new issue