mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 08:53:41 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into red
This commit is contained in:
commit
31230e81bb
62 changed files with 2265 additions and 1403 deletions
assignment-client/src/audio
cmake/externals/openvr
domain-server
resources
src
examples
interface
libraries
animation/src
audio/src
entities/src
gl/src/gl
physics/src
PhysicalEntitySimulation.cppPhysicsEngine.cppPhysicsEngine.hThreadSafeDynamicsWorld.cppThreadSafeDynamicsWorld.h
render-utils/src
render/src/render
shared/src
plugins
oculus/src
openvr
tests
unpublishedScripts/DomainContent/CellScience
|
@ -67,8 +67,6 @@ const QString AUDIO_BUFFER_GROUP_KEY = "audio_buffer";
|
|||
|
||||
InboundAudioStream::Settings AudioMixer::_streamSettings;
|
||||
|
||||
bool AudioMixer::_printStreamStats = false;
|
||||
|
||||
bool AudioMixer::_enableFilter = true;
|
||||
|
||||
bool AudioMixer::shouldMute(float quietestFrame) {
|
||||
|
@ -81,13 +79,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
_minAudibilityThreshold(LOUDNESS_TO_DISTANCE_RATIO / 2.0f),
|
||||
_performanceThrottlingRatio(0.0f),
|
||||
_attenuationPerDoublingInDistance(DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE),
|
||||
_noiseMutingThreshold(DEFAULT_NOISE_MUTING_THRESHOLD),
|
||||
_lastPerSecondCallbackTime(usecTimestampNow()),
|
||||
_sendAudioStreamStats(false),
|
||||
_datagramsReadPerCallStats(0, READ_DATAGRAMS_STATS_WINDOW_SECONDS),
|
||||
_timeSpentPerCallStats(0, READ_DATAGRAMS_STATS_WINDOW_SECONDS),
|
||||
_timeSpentPerHashMatchCallStats(0, READ_DATAGRAMS_STATS_WINDOW_SECONDS),
|
||||
_readPendingCallsPerSecondStats(1, READ_DATAGRAMS_STATS_WINDOW_SECONDS)
|
||||
_noiseMutingThreshold(DEFAULT_NOISE_MUTING_THRESHOLD)
|
||||
{
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& packetReceiver = nodeList->getPacketReceiver();
|
||||
|
@ -484,11 +476,22 @@ void AudioMixer::removeHRTFsForFinishedInjector(const QUuid& streamID) {
|
|||
|
||||
nodeList->eachNode([injectorClientData, &streamID](const SharedNodePointer& node){
|
||||
auto listenerClientData = dynamic_cast<AudioMixerClientData*>(node->getLinkedData());
|
||||
listenerClientData->removeHRTFForStream(injectorClientData->getNodeID(), streamID);
|
||||
if (listenerClientData) {
|
||||
listenerClientData->removeHRTFForStream(injectorClientData->getNodeID(), streamID);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QString AudioMixer::percentageForMixStats(int counter) {
|
||||
if (_totalMixes > 0) {
|
||||
float mixPercentage = (float(counter) / _totalMixes) * 100.0f;
|
||||
return QString::number(mixPercentage, 'f', 2);
|
||||
} else {
|
||||
return QString("0.0");
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixer::sendStatsPacket() {
|
||||
static QJsonObject statsObject;
|
||||
|
||||
|
@ -499,11 +502,11 @@ void AudioMixer::sendStatsPacket() {
|
|||
statsObject["avg_listeners_per_frame"] = (float) _sumListeners / (float) _numStatFrames;
|
||||
|
||||
QJsonObject mixStats;
|
||||
mixStats["%_hrtf_mixes"] = (_totalMixes > 0) ? (_hrtfRenders / _totalMixes) * 100.0f : 0;
|
||||
mixStats["%_hrtf_silent_mixes"] = (_totalMixes > 0) ? (_hrtfSilentRenders / _totalMixes) * 100.0f : 0;
|
||||
mixStats["%_hrtf_struggle_mixes"] = (_totalMixes > 0) ? (_hrtfStruggleRenders / _totalMixes) * 100.0f : 0;
|
||||
mixStats["%_manual_stereo_mixes"] = (_totalMixes > 0) ? (_manualStereoMixes / _totalMixes) * 100.0f : 0;
|
||||
mixStats["%_manual_echo_mixes"] = (_totalMixes > 0) ? (_manualEchoMixes / _totalMixes) * 100.0f : 0;
|
||||
mixStats["%_hrtf_mixes"] = percentageForMixStats(_hrtfRenders);
|
||||
mixStats["%_hrtf_silent_mixes"] = percentageForMixStats(_hrtfSilentRenders);
|
||||
mixStats["%_hrtf_struggle_mixes"] = percentageForMixStats(_hrtfStruggleRenders);
|
||||
mixStats["%_manual_stereo_mixes"] = percentageForMixStats(_manualStereoMixes);
|
||||
mixStats["%_manual_echo_mixes"] = percentageForMixStats(_manualEchoMixes);
|
||||
|
||||
mixStats["total_mixes"] = _totalMixes;
|
||||
mixStats["avg_mixes_per_block"] = _totalMixes / _numStatFrames;
|
||||
|
@ -519,42 +522,6 @@ void AudioMixer::sendStatsPacket() {
|
|||
_totalMixes = 0;
|
||||
_numStatFrames = 0;
|
||||
|
||||
QJsonObject readPendingDatagramStats;
|
||||
|
||||
QJsonObject rpdCallsStats;
|
||||
rpdCallsStats["calls_per_sec_avg_30s"] = _readPendingCallsPerSecondStats.getWindowAverage();
|
||||
rpdCallsStats["calls_last_sec"] = _readPendingCallsPerSecondStats.getLastCompleteIntervalStats().getSum() + 0.5;
|
||||
|
||||
readPendingDatagramStats["calls"] = rpdCallsStats;
|
||||
|
||||
QJsonObject packetsPerCallStats;
|
||||
packetsPerCallStats["avg_30s"] = _datagramsReadPerCallStats.getWindowAverage();
|
||||
packetsPerCallStats["avg_1s"] = _datagramsReadPerCallStats.getLastCompleteIntervalStats().getAverage();
|
||||
|
||||
readPendingDatagramStats["packets_per_call"] = packetsPerCallStats;
|
||||
|
||||
QJsonObject packetsTimePerCallStats;
|
||||
packetsTimePerCallStats["usecs_per_call_avg_30s"] = _timeSpentPerCallStats.getWindowAverage();
|
||||
packetsTimePerCallStats["usecs_per_call_avg_1s"] = _timeSpentPerCallStats.getLastCompleteIntervalStats().getAverage();
|
||||
packetsTimePerCallStats["prct_time_in_call_30s"] =
|
||||
_timeSpentPerCallStats.getWindowSum() / (READ_DATAGRAMS_STATS_WINDOW_SECONDS * USECS_PER_SECOND) * 100.0;
|
||||
packetsTimePerCallStats["prct_time_in_call_1s"] =
|
||||
_timeSpentPerCallStats.getLastCompleteIntervalStats().getSum() / USECS_PER_SECOND * 100.0;
|
||||
|
||||
readPendingDatagramStats["packets_time_per_call"] = packetsTimePerCallStats;
|
||||
|
||||
QJsonObject hashMatchTimePerCallStats;
|
||||
hashMatchTimePerCallStats["usecs_per_hashmatch_avg_30s"] = _timeSpentPerHashMatchCallStats.getWindowAverage();
|
||||
hashMatchTimePerCallStats["usecs_per_hashmatch_avg_1s"]
|
||||
= _timeSpentPerHashMatchCallStats.getLastCompleteIntervalStats().getAverage();
|
||||
hashMatchTimePerCallStats["prct_time_in_hashmatch_30s"]
|
||||
= _timeSpentPerHashMatchCallStats.getWindowSum() / (READ_DATAGRAMS_STATS_WINDOW_SECONDS*USECS_PER_SECOND) * 100.0;
|
||||
hashMatchTimePerCallStats["prct_time_in_hashmatch_1s"]
|
||||
= _timeSpentPerHashMatchCallStats.getLastCompleteIntervalStats().getSum() / USECS_PER_SECOND * 100.0;
|
||||
readPendingDatagramStats["hashmatch_time_per_call"] = hashMatchTimePerCallStats;
|
||||
|
||||
statsObject["read_pending_datagrams"] = readPendingDatagramStats;
|
||||
|
||||
// add stats for each listerner
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
QJsonObject listenerStats;
|
||||
|
@ -680,12 +647,6 @@ void AudioMixer::broadcastMixes() {
|
|||
++framesSinceCutoffEvent;
|
||||
}
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
if (now - _lastPerSecondCallbackTime > USECS_PER_SECOND) {
|
||||
perSecondActions();
|
||||
_lastPerSecondCallbackTime = now;
|
||||
}
|
||||
|
||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||
|
||||
if (node->getLinkedData()) {
|
||||
|
@ -741,10 +702,11 @@ void AudioMixer::broadcastMixes() {
|
|||
nodeList->sendPacket(std::move(mixPacket), *node);
|
||||
nodeData->incrementOutgoingMixedAudioSequenceNumber();
|
||||
|
||||
// send an audio stream stats packet if it's time
|
||||
if (_sendAudioStreamStats) {
|
||||
static const int FRAMES_PER_SECOND = int(ceilf(1.0f / AudioConstants::NETWORK_FRAME_SECS));
|
||||
|
||||
// send an audio stream stats packet to the client approximately every second
|
||||
if (nextFrame % FRAMES_PER_SECOND == 0) {
|
||||
nodeData->sendAudioStreamStatsPackets(node);
|
||||
_sendAudioStreamStats = false;
|
||||
}
|
||||
|
||||
++_sumListeners;
|
||||
|
@ -772,64 +734,6 @@ void AudioMixer::broadcastMixes() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioMixer::perSecondActions() {
|
||||
_sendAudioStreamStats = true;
|
||||
|
||||
int callsLastSecond = _datagramsReadPerCallStats.getCurrentIntervalSamples();
|
||||
_readPendingCallsPerSecondStats.update(callsLastSecond);
|
||||
|
||||
if (_printStreamStats) {
|
||||
|
||||
printf("\n================================================================================\n\n");
|
||||
|
||||
printf(" readPendingDatagram() calls per second | avg: %.2f, avg_30s: %.2f, last_second: %d\n",
|
||||
_readPendingCallsPerSecondStats.getAverage(),
|
||||
_readPendingCallsPerSecondStats.getWindowAverage(),
|
||||
callsLastSecond);
|
||||
|
||||
printf(" Datagrams read per call | avg: %.2f, avg_30s: %.2f, last_second: %.2f\n",
|
||||
_datagramsReadPerCallStats.getAverage(),
|
||||
_datagramsReadPerCallStats.getWindowAverage(),
|
||||
_datagramsReadPerCallStats.getCurrentIntervalAverage());
|
||||
|
||||
printf(" Usecs spent per readPendingDatagram() call | avg: %.2f, avg_30s: %.2f, last_second: %.2f\n",
|
||||
_timeSpentPerCallStats.getAverage(),
|
||||
_timeSpentPerCallStats.getWindowAverage(),
|
||||
_timeSpentPerCallStats.getCurrentIntervalAverage());
|
||||
|
||||
printf(" Usecs spent per packetVersionAndHashMatch() call | avg: %.2f, avg_30s: %.2f, last_second: %.2f\n",
|
||||
_timeSpentPerHashMatchCallStats.getAverage(),
|
||||
_timeSpentPerHashMatchCallStats.getWindowAverage(),
|
||||
_timeSpentPerHashMatchCallStats.getCurrentIntervalAverage());
|
||||
|
||||
double WINDOW_LENGTH_USECS = READ_DATAGRAMS_STATS_WINDOW_SECONDS * USECS_PER_SECOND;
|
||||
|
||||
printf(" %% time spent in readPendingDatagram() calls | avg_30s: %.6f%%, last_second: %.6f%%\n",
|
||||
_timeSpentPerCallStats.getWindowSum() / WINDOW_LENGTH_USECS * 100.0,
|
||||
_timeSpentPerCallStats.getCurrentIntervalSum() / USECS_PER_SECOND * 100.0);
|
||||
|
||||
printf("%% time spent in packetVersionAndHashMatch() calls: | avg_30s: %.6f%%, last_second: %.6f%%\n",
|
||||
_timeSpentPerHashMatchCallStats.getWindowSum() / WINDOW_LENGTH_USECS * 100.0,
|
||||
_timeSpentPerHashMatchCallStats.getCurrentIntervalSum() / USECS_PER_SECOND * 100.0);
|
||||
|
||||
DependencyManager::get<NodeList>()->eachNode([](const SharedNodePointer& node) {
|
||||
if (node->getLinkedData()) {
|
||||
AudioMixerClientData* nodeData = (AudioMixerClientData*)node->getLinkedData();
|
||||
|
||||
if (node->getType() == NodeType::Agent && node->getActiveSocket()) {
|
||||
printf("\nStats for agent %s --------------------------------\n",
|
||||
node->getUUID().toString().toLatin1().data());
|
||||
nodeData->printUpstreamDownstreamStats();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_datagramsReadPerCallStats.currentIntervalComplete();
|
||||
_timeSpentPerCallStats.currentIntervalComplete();
|
||||
_timeSpentPerHashMatchCallStats.currentIntervalComplete();
|
||||
}
|
||||
|
||||
void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) {
|
||||
if (settingsObject.contains(AUDIO_BUFFER_GROUP_KEY)) {
|
||||
QJsonObject audioBufferGroupObject = settingsObject[AUDIO_BUFFER_GROUP_KEY].toObject();
|
||||
|
@ -894,12 +798,6 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) {
|
|||
} else {
|
||||
qDebug() << "Repetition with fade disabled";
|
||||
}
|
||||
|
||||
const QString PRINT_STREAM_STATS_JSON_KEY = "print_stream_stats";
|
||||
_printStreamStats = audioBufferGroupObject[PRINT_STREAM_STATS_JSON_KEY].toBool();
|
||||
if (_printStreamStats) {
|
||||
qDebug() << "Stream stats will be printed to stdout";
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsObject.contains(AUDIO_ENV_GROUP_KEY)) {
|
||||
|
|
|
@ -71,6 +71,8 @@ private:
|
|||
|
||||
void perSecondActions();
|
||||
|
||||
QString percentageForMixStats(int counter);
|
||||
|
||||
bool shouldMute(float quietestFrame);
|
||||
|
||||
void parseSettingsObject(const QJsonObject& settingsObject);
|
||||
|
@ -108,19 +110,7 @@ private:
|
|||
|
||||
static InboundAudioStream::Settings _streamSettings;
|
||||
|
||||
static bool _printStreamStats;
|
||||
static bool _enableFilter;
|
||||
|
||||
quint64 _lastPerSecondCallbackTime;
|
||||
|
||||
bool _sendAudioStreamStats;
|
||||
|
||||
// stats
|
||||
MovingMinMaxAvg<int> _datagramsReadPerCallStats; // update with # of datagrams read for each readPendingDatagrams call
|
||||
MovingMinMaxAvg<quint64> _timeSpentPerCallStats; // update with usecs spent inside each readPendingDatagrams call
|
||||
MovingMinMaxAvg<quint64> _timeSpentPerHashMatchCallStats; // update with usecs spent inside each packetVersionAndHashMatch call
|
||||
|
||||
MovingMinMaxAvg<int> _readPendingCallsPerSecondStats; // update with # of readPendingDatagrams calls in the last second
|
||||
};
|
||||
|
||||
#endif // hifi_AudioMixer_h
|
||||
|
|
|
@ -311,48 +311,3 @@ QJsonObject AudioMixerClientData::getAudioStreamStats() {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioMixerClientData::printUpstreamDownstreamStats() {
|
||||
auto streamsCopy = getAudioStreams();
|
||||
|
||||
// print the upstream (mic stream) stats if the mic stream exists
|
||||
auto it = streamsCopy.find(QUuid());
|
||||
if (it != streamsCopy.end()) {
|
||||
printf("Upstream:\n");
|
||||
printAudioStreamStats(it->second->getAudioStreamStats());
|
||||
}
|
||||
// print the downstream stats if they contain valid info
|
||||
if (_downstreamAudioStreamStats._packetStreamStats._received > 0) {
|
||||
printf("Downstream:\n");
|
||||
printAudioStreamStats(_downstreamAudioStreamStats);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixerClientData::printAudioStreamStats(const AudioStreamStats& streamStats) const {
|
||||
printf(" Packet loss | overall: %5.2f%% (%d lost), last_30s: %5.2f%% (%d lost)\n",
|
||||
(double)(streamStats._packetStreamStats.getLostRate() * 100.0f),
|
||||
streamStats._packetStreamStats._lost,
|
||||
(double)(streamStats._packetStreamWindowStats.getLostRate() * 100.0f),
|
||||
streamStats._packetStreamWindowStats._lost);
|
||||
|
||||
printf(" Ringbuffer frames | desired: %u, avg_available(10s): %u, available: %u\n",
|
||||
streamStats._desiredJitterBufferFrames,
|
||||
streamStats._framesAvailableAverage,
|
||||
streamStats._framesAvailable);
|
||||
|
||||
printf(" Ringbuffer stats | starves: %u, prev_starve_lasted: %u, frames_dropped: %u, overflows: %u\n",
|
||||
streamStats._starveCount,
|
||||
streamStats._consecutiveNotMixedCount,
|
||||
streamStats._framesDropped,
|
||||
streamStats._overflowCount);
|
||||
|
||||
printf(" Inter-packet timegaps (overall) | min: %9s, max: %9s, avg: %9s\n",
|
||||
formatUsecTime(streamStats._timeGapMin).toLatin1().data(),
|
||||
formatUsecTime(streamStats._timeGapMax).toLatin1().data(),
|
||||
formatUsecTime(streamStats._timeGapAverage).toLatin1().data());
|
||||
|
||||
printf(" Inter-packet timegaps (last 30s) | min: %9s, max: %9s, avg: %9s\n",
|
||||
formatUsecTime(streamStats._timeGapWindowMin).toLatin1().data(),
|
||||
formatUsecTime(streamStats._timeGapWindowMax).toLatin1().data(),
|
||||
formatUsecTime(streamStats._timeGapWindowAverage).toLatin1().data());
|
||||
}
|
||||
|
|
|
@ -58,14 +58,9 @@ public:
|
|||
void incrementOutgoingMixedAudioSequenceNumber() { _outgoingMixedAudioSequenceNumber++; }
|
||||
quint16 getOutgoingSequenceNumber() const { return _outgoingMixedAudioSequenceNumber; }
|
||||
|
||||
void printUpstreamDownstreamStats();
|
||||
|
||||
signals:
|
||||
void injectorStreamFinished(const QUuid& streamIdentifier);
|
||||
|
||||
private:
|
||||
void printAudioStreamStats(const AudioStreamStats& streamStats) const;
|
||||
|
||||
private:
|
||||
QReadWriteLock _streamsLock;
|
||||
AudioStreamMap _audioStreams; // microphone stream from avatar is stored under key of null UUID
|
||||
|
|
4
cmake/externals/openvr/CMakeLists.txt
vendored
4
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/ValveSoftware/openvr/archive/v0.9.12.zip
|
||||
URL_MD5 c08dced68ce4e341e1467e6814ae419d
|
||||
URL https://github.com/ValveSoftware/openvr/archive/v0.9.15.zip
|
||||
URL_MD5 0ff8560b49b6da1150fcc47360e8ceca
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -366,14 +366,6 @@
|
|||
"help": "Dropped frames and mixing during starves repeat the last frame, eventually fading to silence",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "print_stream_stats",
|
||||
"type": "checkbox",
|
||||
"label": "Print Stream Stats",
|
||||
"help": "Audio upstream and downstream stats of each agent printed to audio-mixer stdout",
|
||||
"default": false,
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -92,6 +92,12 @@ tr.new-row {
|
|||
background-color: #dff0d8;
|
||||
}
|
||||
|
||||
.graphable-stat {
|
||||
text-align: center;
|
||||
color: #5286BC;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.highchart-modal .modal-dialog {
|
||||
width: 650px;
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link href="/css/bootstrap.min.css" rel="stylesheet" media="screen">
|
||||
<link href="/stats/css/json.human.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/style.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/sweetalert.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/bootstrap-switch.min.css" rel="stylesheet" media="screen">
|
||||
<link href="/stats/css/json.human.css" rel="stylesheet" media="screen">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
$(document).ready(function(){
|
||||
|
||||
|
||||
var currentHighchart;
|
||||
|
||||
// setup a function to grab the nodeStats
|
||||
|
@ -17,12 +17,22 @@ $(document).ready(function(){
|
|||
var stats = JsonHuman.format(json);
|
||||
|
||||
$('#stats-container').html(stats);
|
||||
|
||||
// add the clickable class to anything that looks like a number
|
||||
$('.jh-value span').each(function(val){
|
||||
console.log(val);
|
||||
if (!isNaN($(this).text())) {
|
||||
// this looks like a number - give it the clickable class so we can get graphs for it
|
||||
$(this).addClass('graphable-stat');
|
||||
}
|
||||
});
|
||||
|
||||
if (currentHighchart) {
|
||||
// get the current time to set with the point
|
||||
var x = (new Date()).getTime();
|
||||
|
||||
// get the last value using underscore-keypath
|
||||
var y = _(json).valueForKeyPath(graphKeypath);
|
||||
var y = Number(_(json).valueForKeyPath(graphKeypath));
|
||||
|
||||
// start shifting the chart once we hit 20 data points
|
||||
var shift = currentHighchart.series[0].data.length > 20;
|
||||
|
@ -91,7 +101,7 @@ $(document).ready(function(){
|
|||
}
|
||||
|
||||
// handle clicks on numerical values - this lets the user show a line graph in a modal
|
||||
$('#stats-container').on('click', '.jh-type-number', function(){
|
||||
$('#stats-container').on('click', '.graphable-stat', function(){
|
||||
graphKeypath = $(this).data('keypath');
|
||||
|
||||
// setup the new graph modal
|
||||
|
|
|
@ -55,6 +55,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_oauthProviderURL(),
|
||||
_oauthClientID(),
|
||||
_hostname(),
|
||||
_ephemeralACScripts(),
|
||||
_webAuthenticationStateSet(),
|
||||
_cookieSessionHash(),
|
||||
_automaticNetworkingSetting(),
|
||||
|
@ -1211,13 +1212,14 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) {
|
||||
// we have a matching assignment and it is for the right type, have the HTTP manager handle it
|
||||
// via correct URL for the script so the client can download
|
||||
QFile scriptFile(pathForAssignmentScript(matchingAssignment->getUUID()));
|
||||
const auto it = _ephemeralACScripts.find(matchingAssignment->getUUID());
|
||||
|
||||
if (scriptFile.exists() && scriptFile.open(QIODevice::ReadOnly)) {
|
||||
connection->respond(HTTPConnection::StatusCode200, scriptFile.readAll(), "application/javascript");
|
||||
if (it != _ephemeralACScripts.end()) {
|
||||
connection->respond(HTTPConnection::StatusCode200, it->second, "application/javascript");
|
||||
} else {
|
||||
connection->respond(HTTPConnection::StatusCode404, "Resource not found.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1387,29 +1389,15 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
|
||||
|
||||
for (int i = 0; i < numInstances; i++) {
|
||||
|
||||
// create an assignment for this saved script
|
||||
Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType, assignmentPool);
|
||||
|
||||
QString newPath = pathForAssignmentScript(scriptAssignment->getUUID());
|
||||
_ephemeralACScripts[scriptAssignment->getUUID()] = formData[0].second;
|
||||
|
||||
// create a file with the GUID of the assignment in the script host location
|
||||
QFile scriptFile(newPath);
|
||||
if (scriptFile.open(QIODevice::WriteOnly)) {
|
||||
scriptFile.write(formData[0].second);
|
||||
|
||||
qDebug() << qPrintable(QString("Saved a script for assignment at %1%2")
|
||||
.arg(newPath).arg(assignmentPool == emptyPool ? "" : " - pool is " + assignmentPool));
|
||||
|
||||
// add the script assigment to the assignment queue
|
||||
SharedAssignmentPointer sharedScriptedAssignment(scriptAssignment);
|
||||
_unfulfilledAssignments.enqueue(sharedScriptedAssignment);
|
||||
_allAssignments.insert(sharedScriptedAssignment->getUUID(), sharedScriptedAssignment);
|
||||
} else {
|
||||
// unable to save script for assignment - we shouldn't be here but debug it out
|
||||
qDebug() << "Unable to save a script for assignment at" << newPath;
|
||||
qDebug() << "Script will not be added to queue";
|
||||
}
|
||||
// add the script assigment to the assignment queue
|
||||
SharedAssignmentPointer sharedScriptedAssignment(scriptAssignment);
|
||||
_unfulfilledAssignments.enqueue(sharedScriptedAssignment);
|
||||
_allAssignments.insert(sharedScriptedAssignment->getUUID(), sharedScriptedAssignment);
|
||||
}
|
||||
|
||||
// respond with a 200 code for successful upload
|
||||
|
|
|
@ -142,6 +142,8 @@ private:
|
|||
QString _oauthClientSecret;
|
||||
QString _hostname;
|
||||
|
||||
std::unordered_map<QUuid, QByteArray> _ephemeralACScripts;
|
||||
|
||||
QSet<QUuid> _webAuthenticationStateSet;
|
||||
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ function AttachedEntitiesManager() {
|
|||
this.checkIfWearable = function(grabbedEntity, releasedFromJoint) {
|
||||
var allowedJoints = getEntityCustomData('wearable', grabbedEntity, DEFAULT_WEARABLE_DATA).joints;
|
||||
|
||||
var props = Entities.getEntityProperties(grabbedEntity, ["position", "parentID"]);
|
||||
var props = Entities.getEntityProperties(grabbedEntity, ["position", "parentID", "parentJointIndex"]);
|
||||
if (props.parentID === NULL_UUID || props.parentID === MyAvatar.sessionUUID) {
|
||||
var bestJointName = "";
|
||||
var bestJointIndex = -1;
|
||||
|
@ -192,10 +192,14 @@ function AttachedEntitiesManager() {
|
|||
}
|
||||
Entities.editEntity(grabbedEntity, wearProps);
|
||||
} else if (props.parentID != NULL_UUID) {
|
||||
// drop the entity with no parent (not on the avatar)
|
||||
Entities.editEntity(grabbedEntity, {
|
||||
parentID: NULL_UUID
|
||||
});
|
||||
// drop the entity and set it to have no parent (not on the avatar), unless it's being equipped in a hand.
|
||||
if (props.parentID === MyAvatar.sessionUUID &&
|
||||
(props.parentJointIndex == MyAvatar.getJointIndex("RightHand") ||
|
||||
props.parentJointIndex == MyAvatar.getJointIndex("LeftHand"))) {
|
||||
// this is equipped on a hand -- don't clear the parent.
|
||||
} else {
|
||||
Entities.editEntity(grabbedEntity, { parentID: NULL_UUID });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -946,13 +946,15 @@ function MyController(hand) {
|
|||
return;
|
||||
}
|
||||
// near grab or equip with action
|
||||
if (near && (grabbableData.refCount < 1 || entityHasActions(this.grabbedEntity))) {
|
||||
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
|
||||
var refCount = ("refCount" in grabData) ? grabData.refCount : 0;
|
||||
if (near && (refCount < 1 || entityHasActions(this.grabbedEntity))) {
|
||||
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
|
||||
return;
|
||||
}
|
||||
// far grab or equip with action
|
||||
if ((isPhysical || this.state == STATE_EQUIP_SEARCHING) && !near) {
|
||||
if (entityIsGrabbedByOther(intersection.entityID)) {
|
||||
if (entityIsGrabbedByOther(this.grabbedEntity)) {
|
||||
// don't distance grab something that is already grabbed.
|
||||
if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': already grabbed by another.");
|
||||
|
@ -1449,11 +1451,12 @@ function MyController(hand) {
|
|||
return;
|
||||
}
|
||||
|
||||
var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID"]);
|
||||
var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position"]);
|
||||
if (props.parentID == MyAvatar.sessionUUID &&
|
||||
Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) {
|
||||
// for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip.
|
||||
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand.");
|
||||
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." +
|
||||
props.parentID + " " + vec3toStr(props.position));
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed(this.state == STATE_NEAR_GRABBING ? "releaseGrab" : "releaseEquip",
|
||||
[JSON.stringify(this.hand)]);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(function () {
|
||||
// See tests/performance/tribbles.js
|
||||
Script.include("../libraries/virtualBaton.js");
|
||||
var dimensions, oldColor, entityID,
|
||||
editRate = 60,
|
||||
moveRate = 1,
|
||||
|
@ -7,7 +8,8 @@
|
|||
accumulated = 0,
|
||||
increment = {red: 1, green: 1, blue: 1},
|
||||
hasUpdate = false,
|
||||
shutdown = false;
|
||||
shutdown = false,
|
||||
baton;
|
||||
function nextWavelength(color) {
|
||||
var old = oldColor[color];
|
||||
if (old === 255) {
|
||||
|
@ -27,13 +29,37 @@
|
|||
accumulated = 0;
|
||||
}
|
||||
}
|
||||
function randomCentered() { return Math.random() - 0.5; }
|
||||
function randomVector() { return {x: randomCentered() * dimensions.x, y: randomCentered() * dimensions.y, z: randomCentered() * dimensions.z}; }
|
||||
function randomCentered() {
|
||||
return Math.random() - 0.5;
|
||||
}
|
||||
function randomVector() {
|
||||
return {x: randomCentered() * dimensions.x, y: randomCentered() * dimensions.y, z: randomCentered() * dimensions.z};
|
||||
}
|
||||
function move() {
|
||||
var newData = {velocity: Vec3.sum({x: 0, y: 1, z: 0}, randomVector()), angularVelocity: Vec3.multiply(Math.PI, randomVector())};
|
||||
var nextChange = Math.ceil(Math.random() * 2000 / moveRate);
|
||||
Entities.editEntity(entityID, newData);
|
||||
if (!shutdown) { Script.setTimeout(move, nextChange); }
|
||||
if (!shutdown) {
|
||||
Script.setTimeout(move, nextChange);
|
||||
}
|
||||
}
|
||||
function startUpdate() {
|
||||
print('startUpdate', entityID);
|
||||
hasUpdate = true;
|
||||
Script.update.connect(update);
|
||||
}
|
||||
function stopUpdate() {
|
||||
print('stopUpdate', entityID, hasUpdate);
|
||||
if (!hasUpdate) {
|
||||
return;
|
||||
}
|
||||
hasUpdate = false;
|
||||
Script.update.disconnect(update);
|
||||
}
|
||||
function stopUpdateAndReclaim() {
|
||||
print('stopUpdateAndReclaim', entityID);
|
||||
stopUpdate();
|
||||
baton.claim(startUpdate, stopUpdateAndReclaim);
|
||||
}
|
||||
this.preload = function (givenEntityID) {
|
||||
entityID = givenEntityID;
|
||||
|
@ -41,26 +67,35 @@
|
|||
var userData = properties.userData && JSON.parse(properties.userData);
|
||||
var moveTimeout = userData ? userData.moveTimeout : 0;
|
||||
var editTimeout = userData ? userData.editTimeout : 0;
|
||||
var debug = (userData && userData.debug) || {};
|
||||
editRate = (userData && userData.editRate) || editRate;
|
||||
moveRate = (moveRate && userData.moveRate) || moveRate;
|
||||
oldColor = properties.color;
|
||||
dimensions = Vec3.multiply(scale, properties.dimensions);
|
||||
baton = virtualBaton({
|
||||
batonName: 'io.highfidelity.tribble:' + entityID, // One winner for each entity
|
||||
debugFlow: debug.flow,
|
||||
debugSend: debug.send,
|
||||
debugReceive: debug.receive
|
||||
});
|
||||
if (editTimeout) {
|
||||
hasUpdate = true;
|
||||
Script.update.connect(update);
|
||||
baton.claim(startUpdate, stopUpdateAndReclaim);
|
||||
if (editTimeout > 0) {
|
||||
Script.setTimeout(function () { Script.update.disconnect(update); hasUpdate = false; }, editTimeout * 1000);
|
||||
Script.setTimeout(stopUpdate, editTimeout * 1000);
|
||||
}
|
||||
}
|
||||
if (moveTimeout) {
|
||||
Script.setTimeout(move, 1000);
|
||||
if (moveTimeout > 0) {
|
||||
Script.setTimeout(function () { shutdown = true; }, moveTimeout * 1000);
|
||||
Script.setTimeout(function () {
|
||||
shutdown = true;
|
||||
}, moveTimeout * 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.unload = function () {
|
||||
baton.unload();
|
||||
shutdown = true;
|
||||
if (hasUpdate) { Script.update.disconnect(update); }
|
||||
stopUpdate();
|
||||
};
|
||||
})
|
||||
|
|
381
examples/libraries/virtualBaton.js
Normal file
381
examples/libraries/virtualBaton.js
Normal file
|
@ -0,0 +1,381 @@
|
|||
"use strict";
|
||||
/*jslint nomen: true, plusplus: true, vars: true */
|
||||
/*global Messages, Script, MyAvatar, AvatarList, Entities, print */
|
||||
|
||||
// Created by Howard Stearns
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// Allows cooperating scripts to pass a "virtual baton" between them,
|
||||
// which is useful when part of a script should only be executed by
|
||||
// the one participant that is holding this particular baton.
|
||||
//
|
||||
// A virtual baton is simply any string agreed upon by the scripts
|
||||
// that use it. Only one script at a time can hold the baton, and it
|
||||
// holds it until that script releases it, or the other scripts
|
||||
// determine that the holding script is not responding. The script
|
||||
// automatically determines who among claimants has the baton, if anyone,
|
||||
// and holds an "election" if necessary.
|
||||
//
|
||||
// See entityScript/tribble.js as an example, and the functions
|
||||
// virtualBaton(), claim(), release().
|
||||
//
|
||||
|
||||
// Answers a new virtualBaton for the given parameters, of which 'key'
|
||||
// is required.
|
||||
function virtualBatonf(options) {
|
||||
// Answer averages (number +/- variability). Avoids having everyone act in lockstep.
|
||||
function randomize(number, variability) {
|
||||
var allowedDeviation = number * variability; // one side of the deviation range
|
||||
var allowedDeviationRange = allowedDeviation * 2; // total range for +/- deviation
|
||||
var randomDeviation = Math.random() * allowedDeviationRange;
|
||||
var result = number - allowedDeviation + randomDeviation;
|
||||
return result;
|
||||
}
|
||||
// Allow testing outside in a harness outside of High Fidelity.
|
||||
// See sourceCodeSandbox/tests/mocha/test/testVirtualBaton.js
|
||||
var globals = options.globals || {},
|
||||
messages = globals.Messages || Messages,
|
||||
myAvatar = globals.MyAvatar || MyAvatar,
|
||||
avatarList = globals.AvatarList || AvatarList,
|
||||
entities = globals.Entities || Entities,
|
||||
timers = globals.Script || Script,
|
||||
log = globals.print || print;
|
||||
|
||||
var batonName = options.batonName, // The identify of the baton.
|
||||
// instanceId is the identify of this particular copy of the script among all copies using the same batonName
|
||||
// in the domain. For example, if you wanted only one entity among multiple entity scripts to hold the baton,
|
||||
// you could specify virtualBaton({batonName: 'someBatonName', instanceId: MyAvatar.sessionUUID + entityID}).
|
||||
instanceId = options.instanceId || myAvatar.sessionUUID,
|
||||
// virtualBaton() returns the exports object with properties. You can pass in an object to be side-effected.
|
||||
exports = options.exports || {},
|
||||
// Handy to set false if we believe the optimizations are wrong, or to use both values in a test harness.
|
||||
useOptimizations = (options.useOptimizations === undefined) ? true : options.useOptimizations,
|
||||
electionTimeout = options.electionTimeout || randomize(500, 0.2), // ms. If no winner in this time, hold a new election.
|
||||
recheckInterval = options.recheckInterval || randomize(500, 0.2), // ms. Check that winners remain connected.
|
||||
// If you supply your own instanceId, you might also supply a connectionTest that answers
|
||||
// truthy iff the given id is still valid and connected, and is run at recheckInterval. You
|
||||
// can use exports.validId (see below), and the default answers truthy if id is valid or a
|
||||
// concatenation of two valid ids. (This handles the most common cases of instanceId being
|
||||
// either (the default) MyAvatar.sessionUUID, an entityID, or the concatenation (in either
|
||||
// order) of both.)
|
||||
connectionTest = options.connectionTest || function connectionTest(id) {
|
||||
var idLength = 38;
|
||||
if (id.length === idLength) {
|
||||
return exports.validId(id);
|
||||
}
|
||||
return (id.length === 2 * idLength) && exports.validId(id.slice(0, idLength)) && exports.validId(id.slice(idLength));
|
||||
};
|
||||
|
||||
if (!batonName) {
|
||||
throw new Error("A virtualBaton must specify a batonName.");
|
||||
}
|
||||
// Truthy if id exists as either a connected avatar or valid entity.
|
||||
exports.validId = function validId(id) {
|
||||
var avatar = avatarList.getAvatar(id);
|
||||
if (avatar && (avatar.sessionUUID === id)) {
|
||||
return true;
|
||||
}
|
||||
var properties = entities.getEntityProperties(id, ['type']);
|
||||
return properties && properties.type;
|
||||
};
|
||||
|
||||
// Various logging, controllable through options.
|
||||
function debug() { // Display the arguments not just [Object object].
|
||||
log.apply(null, [].map.call(arguments, JSON.stringify));
|
||||
}
|
||||
function debugFlow() {
|
||||
if (options.debugFlow) {
|
||||
debug.apply(null, arguments);
|
||||
}
|
||||
}
|
||||
function debugSend(destination, operation, data) {
|
||||
if (options.debugSend) {
|
||||
debug('baton:', batonName, instanceId, 's=>', destination, operation, data);
|
||||
}
|
||||
}
|
||||
function debugReceive(senderID, operation, data) { // senderID is client sessionUUID -- not necessarily instanceID!
|
||||
if (options.debugReceive) {
|
||||
debug('baton:', batonName, senderID, '=>r', instanceId, operation, data);
|
||||
}
|
||||
}
|
||||
|
||||
// Messages: Just synactic sugar for hooking things up to Messages system.
|
||||
// We create separate subchannel strings for each operation within our general channelKey, instead of using
|
||||
// a switch in the receiver.
|
||||
var channelKey = "io.highfidelity.virtualBaton:" + batonName,
|
||||
subchannelHandlers = {}, // Message channel string => {receiver, op}
|
||||
subchannelKeys = {}; // operation => Message channel string
|
||||
function subchannelKey(operation) {
|
||||
return channelKey + ':' + operation;
|
||||
}
|
||||
function receive(operation, handler) { // Record a handler for an operation on our channelKey
|
||||
var subKey = subchannelKey(operation);
|
||||
subchannelHandlers[subKey] = {receiver: handler, op: operation};
|
||||
subchannelKeys[operation] = subKey;
|
||||
messages.subscribe(subKey);
|
||||
}
|
||||
function sendHelper(subchannel, data) {
|
||||
var message = JSON.stringify(data);
|
||||
messages.sendMessage(subchannel, message);
|
||||
}
|
||||
function send1(operation, destination, data) { // Send data for an operation to just one destination on our channelKey.
|
||||
debugSend(destination, operation, data);
|
||||
sendHelper(subchannelKey(operation) + destination, data);
|
||||
}
|
||||
function send(operation, data) { // Send data for an operation on our channelKey.
|
||||
debugSend('-', operation, data);
|
||||
sendHelper(subchannelKeys[operation], data);
|
||||
}
|
||||
function messageHandler(channel, messageString, senderID) {
|
||||
var handler = subchannelHandlers[channel];
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
var data = JSON.parse(messageString);
|
||||
debugReceive(senderID, handler.op, data);
|
||||
handler.receiver(data);
|
||||
}
|
||||
messages.messageReceived.connect(messageHandler);
|
||||
|
||||
var nPromises = 0, nAccepted = 0, electionWatchdog;
|
||||
|
||||
// It would be great if we had a way to know how many subscribers our channel has. Failing that...
|
||||
var nNack = 0, previousNSubscribers = 0, lastGathering = 0, thisTimeout = electionTimeout;
|
||||
function nSubscribers() { // Answer the number of subscribers.
|
||||
// To find nQuorum, we need to know how many scripts are being run using this batonName, which isn't
|
||||
// the same as the number of clients!
|
||||
//
|
||||
// If we overestimate by too much, we may fail to reach consensus, which triggers a new
|
||||
// election proposal, so we take the number of acceptors to be the max(nPromises, nAccepted)
|
||||
// + nNack reported in the previous round.
|
||||
//
|
||||
// If we understimate by too much, there can be different pockets on the Internet that each
|
||||
// believe they have agreement on different holders of the baton, which is precisely what
|
||||
// the virtualBaton is supposed to avoid. Therefore we need to allow 'nack' to gather stragglers.
|
||||
|
||||
var now = Date.now(), elapsed = now - lastGathering;
|
||||
if (elapsed >= thisTimeout) {
|
||||
previousNSubscribers = Math.max(nPromises, nAccepted) + nNack;
|
||||
lastGathering = now;
|
||||
} // ...otherwise we use the previous value unchanged.
|
||||
|
||||
// On startup, we do one proposal that we cannot possibly close, so that we'll
|
||||
// lock things up for the full electionTimeout to gather responses.
|
||||
if (!previousNSubscribers) {
|
||||
var LARGE_INTEGER = Number.MAX_SAFE_INTEGER || (-1 >>> 1); // QT doesn't define the ECMA constant. Max int will do for our purposes.
|
||||
previousNSubscribers = LARGE_INTEGER;
|
||||
}
|
||||
return previousNSubscribers;
|
||||
}
|
||||
|
||||
// MAIN ALGORITHM
|
||||
//
|
||||
// Internally, this uses the Paxos algorith to hold elections.
|
||||
// Alternatively, we could have the message server pick and maintain a winner, but it would
|
||||
// still have to deal with the same issues of verification in the presence of lost/delayed/reordered messages.
|
||||
// Paxos is known to be optimal under these circumstances, except that its best to have a dedicated proposer
|
||||
// (such as the server).
|
||||
function betterNumber(number, best) {
|
||||
return (number.number || 0) > best.number;
|
||||
}
|
||||
// Paxos Proposer behavior
|
||||
var proposalNumber = 0,
|
||||
nQuorum = 0,
|
||||
bestPromise = {number: 0},
|
||||
claimCallback,
|
||||
releaseCallback;
|
||||
function propose() { // Make a new proposal, so that we learn/update the proposalNumber and winner.
|
||||
// Even though we send back a 'nack' if the proposal is obsolete, with network errors
|
||||
// there's no way to know for certain that we've failed. The electionWatchdog will try a new
|
||||
// proposal if we have not been accepted by a quorum after election Timeout.
|
||||
if (electionWatchdog) {
|
||||
// If we had a means of determining nSubscribers other than by counting, we could just
|
||||
// timers.clearTimeout(electionWatchdog) and not return.
|
||||
return;
|
||||
}
|
||||
thisTimeout = randomize(electionTimeout, 0.5); // Note use in nSubcribers.
|
||||
electionWatchdog = timers.setTimeout(function () {
|
||||
electionWatchdog = null;
|
||||
propose();
|
||||
}, thisTimeout);
|
||||
var nAcceptors = nSubscribers();
|
||||
nQuorum = Math.floor(nAcceptors / 2) + 1;
|
||||
|
||||
proposalNumber = Math.max(proposalNumber, bestPromise.number) + 1;
|
||||
debugFlow('baton:', batonName, instanceId, 'propose', proposalNumber,
|
||||
'claim:', !!claimCallback, 'nAcceptors:', nAcceptors, nPromises, nAccepted, nNack);
|
||||
nPromises = nAccepted = nNack = 0;
|
||||
send('prepare!', {number: proposalNumber, proposerId: instanceId});
|
||||
}
|
||||
// We create a distinguished promise subchannel for our id, because promises need only be sent to the proposer.
|
||||
receive('promise' + instanceId, function (data) {
|
||||
if (betterNumber(data, bestPromise)) {
|
||||
bestPromise = data;
|
||||
}
|
||||
if ((data.proposalNumber === proposalNumber) && (++nPromises >= nQuorum)) { // Note check for not being a previous round
|
||||
var answer = {number: data.proposalNumber, proposerId: data.proposerId, winner: bestPromise.winner}; // Not data.number.
|
||||
if (!answer.winner || (answer.winner === instanceId)) { // We get to pick.
|
||||
answer.winner = claimCallback ? instanceId : null;
|
||||
}
|
||||
send('accept!', answer);
|
||||
}
|
||||
});
|
||||
receive('nack' + instanceId, function (data) { // An acceptor reports more recent data...
|
||||
if (data.proposalNumber === proposalNumber) {
|
||||
nNack++; // For updating nQuorum.
|
||||
// IWBNI if we started our next proposal right now/here, but we need a decent nNack count.
|
||||
// Lets save that optimization for another day...
|
||||
}
|
||||
});
|
||||
// Paxos Acceptor behavior
|
||||
var bestProposal = {number: 0}, accepted = {};
|
||||
function acceptedId() {
|
||||
return accepted && accepted.winner;
|
||||
}
|
||||
receive('prepare!', function (data) {
|
||||
var response = {proposalNumber: data.number, proposerId: data.proposerId};
|
||||
if (betterNumber(data, bestProposal)) {
|
||||
bestProposal = data;
|
||||
if (accepted.winner && connectionTest(accepted.winner)) {
|
||||
response.number = accepted.number;
|
||||
response.winner = accepted.winner;
|
||||
}
|
||||
send1('promise', data.proposerId, response);
|
||||
} else {
|
||||
send1('nack', data.proposerId, response);
|
||||
}
|
||||
});
|
||||
receive('accept!', function (data) {
|
||||
if (!betterNumber(bestProposal, data)) {
|
||||
bestProposal = accepted = data; // Update both with current data. Might have missed the proposal earlier.
|
||||
if (useOptimizations) {
|
||||
// The Paxos literature describes every acceptor sending 'accepted' to
|
||||
// every proposer and learner. In our case, these are the same nodes that received
|
||||
// the 'accept!' message, so we can send to just the originating proposer and invoke
|
||||
// our own accepted handler directly.
|
||||
// Note that this optimization cannot be used with Byzantine Paxos (which needs another
|
||||
// multi-broadcast to detect lying and collusion).
|
||||
debugSend('/', 'accepted', data);
|
||||
debugReceive(instanceId, 'accepted', data); // direct on next line, which doesn't get logging.
|
||||
subchannelHandlers[subchannelKey('accepted') + instanceId].receiver(data);
|
||||
if (data.proposerId !== instanceId) { // i.e., we didn't already do it directly on the line above.
|
||||
send1('accepted', data.proposerId, data);
|
||||
}
|
||||
} else {
|
||||
send('accepted', data);
|
||||
}
|
||||
} else {
|
||||
send1('nack', data.proposerId, {proposalNumber: data.number});
|
||||
}
|
||||
});
|
||||
// Paxos Learner behavior.
|
||||
function localRelease() {
|
||||
var callback = releaseCallback;
|
||||
debugFlow('baton:', batonName, 'localRelease', 'callback:', !!releaseCallback);
|
||||
if (!releaseCallback) {
|
||||
return;
|
||||
} // Already released, but we might still receive a stale message. That's ok.
|
||||
releaseCallback = undefined;
|
||||
callback(batonName); // Pass batonName so that clients may use the same handler for different batons.
|
||||
}
|
||||
receive('accepted' + (useOptimizations ? instanceId : ''), function (data) { // See note in 'accept!' regarding use of instanceId here.
|
||||
if (betterNumber(accepted, data)) { // Especially when !useOptimizations, we can receive other acceptances late.
|
||||
return;
|
||||
}
|
||||
var oldAccepted = accepted;
|
||||
debugFlow('baton:', batonName, instanceId, 'accepted', data.number, data.winner);
|
||||
accepted = data;
|
||||
// If we are proposer, make sure we get a quorum of acceptances.
|
||||
if ((data.proposerId === instanceId) && (data.number === proposalNumber) && (++nAccepted >= nQuorum)) {
|
||||
if (electionWatchdog) {
|
||||
timers.clearTimeout(electionWatchdog);
|
||||
electionWatchdog = null;
|
||||
}
|
||||
}
|
||||
// If we are the winner -- regardless of whether we were the proposer.
|
||||
if (acceptedId() === instanceId) {
|
||||
if (claimCallback) {
|
||||
var callback = claimCallback;
|
||||
claimCallback = undefined;
|
||||
callback(batonName);
|
||||
} else if (!releaseCallback) { // We won, but have been released and are no longer interested.
|
||||
// Propose that someone else take the job.
|
||||
timers.setTimeout(propose, 0); // Asynchronous to queue message handling if some are synchronous and others not.
|
||||
}
|
||||
} else if (releaseCallback && (oldAccepted.winner === instanceId)) { // We've been released by someone else!
|
||||
localRelease(); // This can happen if enough people thought we'd disconnected.
|
||||
}
|
||||
});
|
||||
|
||||
// Public Interface
|
||||
//
|
||||
// Registers an intent to hold the baton:
|
||||
// Calls onElection(batonName) once, if you are elected by the scripts
|
||||
// to be the unique holder of the baton, which may be never.
|
||||
// Calls onRelease(batonName) once, if the baton held by you is released,
|
||||
// whether this is by you calling release(), or by losing
|
||||
// an election when you become disconnected.
|
||||
// You may claim again at any time after the start of onRelease
|
||||
// being called.
|
||||
exports.claim = function claim(onElection, onRelease) {
|
||||
debugFlow('baton:', batonName, instanceId, 'claim');
|
||||
if (claimCallback) {
|
||||
log("Ignoring attempt to claim virtualBaton " + batonName + ", which is already waiting for claim.");
|
||||
return;
|
||||
}
|
||||
if (releaseCallback) {
|
||||
log("Ignoring attempt to claim virtualBaton " + batonName + ", which is somehow incorrect released, and that should not happen.");
|
||||
return;
|
||||
}
|
||||
claimCallback = onElection;
|
||||
releaseCallback = onRelease;
|
||||
propose();
|
||||
return exports; // Allows chaining. e.g., var baton = virtualBaton({batonName: 'foo'}.claim(onClaim, onRelease);
|
||||
};
|
||||
// Release the baton you hold, or just log that you are not holding it.
|
||||
exports.release = function release(optionalReplacementOnRelease) {
|
||||
debugFlow('baton:', batonName, instanceId, 'release');
|
||||
if (optionalReplacementOnRelease) { // If you want to change.
|
||||
releaseCallback = optionalReplacementOnRelease;
|
||||
}
|
||||
if (acceptedId() !== instanceId) {
|
||||
log("Ignoring attempt to release virtualBaton " + batonName + ", which is not being held.");
|
||||
return;
|
||||
}
|
||||
localRelease();
|
||||
if (!claimCallback) { // No claim set in release callback.
|
||||
propose();
|
||||
}
|
||||
return exports;
|
||||
};
|
||||
exports.recheckWatchdog = timers.setInterval(function recheck() {
|
||||
var holder = acceptedId(); // If we're waiting and we notice the holder is gone, ...
|
||||
if (holder && claimCallback && !electionWatchdog && !connectionTest(holder)) {
|
||||
bestPromise.winner = null; // used if the quorum agrees that old winner is not there
|
||||
propose(); // ... propose an election.
|
||||
}
|
||||
}, recheckInterval);
|
||||
exports.unload = function unload() { // Disconnect from everything.
|
||||
messages.messageReceived.disconnect(messageHandler);
|
||||
timers.clearInterval(exports.recheckWatchdog);
|
||||
if (electionWatchdog) {
|
||||
timers.clearTimeout(electionWatchdog);
|
||||
}
|
||||
electionWatchdog = claimCallback = releaseCallback = null;
|
||||
Object.keys(subchannelHandlers).forEach(messages.unsubscribe);
|
||||
debugFlow('baton:', batonName, instanceId, 'unload');
|
||||
return exports;
|
||||
};
|
||||
|
||||
// Gather nAcceptors by making two proposals with some gathering time, even without a claim.
|
||||
propose();
|
||||
return exports;
|
||||
}
|
||||
if (typeof module !== 'undefined') { // Allow testing in nodejs.
|
||||
module.exports = virtualBatonf;
|
||||
} else {
|
||||
virtualBaton = virtualBatonf;
|
||||
}
|
|
@ -14,8 +14,8 @@ var Vec3, Quat, MyAvatar, Entities, Camera, Script, print;
|
|||
// The _TIMEOUT parameters can be 0 for no activity, and -1 to be active indefinitely.
|
||||
//
|
||||
|
||||
var NUMBER_TO_CREATE = 200;
|
||||
var LIFETIME = 60; // seconds
|
||||
var NUMBER_TO_CREATE = 100;
|
||||
var LIFETIME = 120; // seconds
|
||||
var EDIT_RATE = 60; // hz
|
||||
var EDIT_TIMEOUT = -1;
|
||||
var MOVE_RATE = 1; // hz
|
||||
|
@ -68,7 +68,8 @@ Script.setInterval(function () {
|
|||
moveTimeout: MOVE_TIMEOUT,
|
||||
moveRate: MOVE_RATE,
|
||||
editTimeout: EDIT_TIMEOUT,
|
||||
editRate: EDIT_RATE
|
||||
editRate: EDIT_RATE,
|
||||
debug: {flow: false, send: false, receive: false}
|
||||
});
|
||||
for (i = 0; (i < numToCreate) && (totalCreated < NUMBER_TO_CREATE); i++) {
|
||||
Entities.addEntity({
|
||||
|
|
66
examples/utilities/tools/render/ConfigSlider.qml
Normal file
66
examples/utilities/tools/render/ConfigSlider.qml
Normal file
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// ConfigSlider.qml
|
||||
// examples/utilities/tools/render
|
||||
//
|
||||
// Created by Zach Pomerantz on 2/8/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 400
|
||||
height: 24
|
||||
property bool integral: false
|
||||
property var config
|
||||
property string property
|
||||
property alias label: labelControl.text
|
||||
property alias min: sliderControl.minimumValue
|
||||
property alias max: sliderControl.maximumValue
|
||||
|
||||
Component.onCompleted: {
|
||||
// Binding favors qml value, so set it first
|
||||
sliderControl.value = root.config[root.property];
|
||||
bindingControl.when = true;
|
||||
}
|
||||
|
||||
Label {
|
||||
id: labelControl
|
||||
text: root.label
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.top: root.top
|
||||
anchors.topMargin: 7
|
||||
}
|
||||
|
||||
Label {
|
||||
text: sliderControl.value.toFixed(root.integral ? 0 : 2)
|
||||
anchors.left: root.left
|
||||
anchors.leftMargin: 140
|
||||
anchors.top: root.top
|
||||
anchors.topMargin: 7
|
||||
}
|
||||
|
||||
Binding {
|
||||
id: bindingControl
|
||||
target: root.config
|
||||
property: root.property
|
||||
value: sliderControl.value
|
||||
when: false
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: sliderControl
|
||||
stepSize: root.integral ? 1.0 : 0.0
|
||||
width: 192
|
||||
height: 20
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.top: root.top
|
||||
anchors.topMargin: 3
|
||||
}
|
||||
}
|
42
examples/utilities/tools/render/debug.js
Normal file
42
examples/utilities/tools/render/debug.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// debug.js
|
||||
// examples/utilities/tools/render
|
||||
//
|
||||
// Zach Pomerantz, created on 1/27/2016.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
oldConfig = Render.toJSON();
|
||||
Render.RenderShadowTask.enabled = true;
|
||||
var RDT = Render.RenderDeferredTask;
|
||||
RDT.AmbientOcclusion.enabled = true;
|
||||
RDT.DebugDeferredBuffer.enabled = false;
|
||||
["DrawOpaqueDeferred", "DrawTransparentDeferred", "DrawOverlay3DOpaque", "DrawOverlay3DTransparent"]
|
||||
.map(function(name) { return RDT[name]; })
|
||||
.forEach(function(job) { job.maxDrawn = job.numDrawn; });
|
||||
|
||||
// Set up the qml ui
|
||||
var qml = Script.resolvePath('main.qml');
|
||||
var window = new OverlayWindow({
|
||||
title: 'Render Engine Configuration',
|
||||
source: qml,
|
||||
width: 400, height: 900,
|
||||
});
|
||||
window.setPosition(25, 50);
|
||||
window.closed.connect(function() { Script.stop(); });
|
||||
|
||||
// Debug buffer sizing
|
||||
var resizing = false;
|
||||
Controller.mousePressEvent.connect(function() { resizing = true; });
|
||||
Controller.mouseReleaseEvent.connect(function() { resizing = false; });
|
||||
Controller.mouseMoveEvent.connect(function(e) { resizing && setDebugBufferSize(e.x); });
|
||||
function setDebugBufferSize(x) {
|
||||
x = (2.0 * (x / Window.innerWidth) - 1.0); // scale
|
||||
x = Math.min(Math.max(-1, x), 1); // clamp
|
||||
Render.RenderDeferredTask.DebugDeferredBuffer.size = {x: x, y: -1, z: 1, w: 1};
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() { Render.fromJSON(oldConfig); } );
|
110
examples/utilities/tools/render/main.qml
Normal file
110
examples/utilities/tools/render/main.qml
Normal file
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// main.qml
|
||||
// examples/utilities/tools/render
|
||||
//
|
||||
// Created by Zach Pomerantz on 2/8/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
Column {
|
||||
spacing: 8
|
||||
|
||||
Repeater {
|
||||
model: [ "Opaque:DrawOpaqueDeferred", "Transparent:DrawTransparentDeferred",
|
||||
"Opaque Overlays:DrawOverlay3DOpaque", "Transparent Overlays:DrawOverlay3DTransparent" ]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
integral: true
|
||||
config: Render.getConfig(modelData.split(":")[1])
|
||||
property: "maxDrawn"
|
||||
max: config.numDrawn
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
CheckBox {
|
||||
text: qsTr("Display Status")
|
||||
onCheckedChanged: { Render.getConfig("DrawStatus").showDisplay = checked }
|
||||
}
|
||||
CheckBox {
|
||||
text: qsTr("Network/Physics Status")
|
||||
onCheckedChanged: { Render.getConfig("DrawStatus").showNetwork = checked }
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
label: qsTr("Tone Mapping Exposure")
|
||||
config: Render.getConfig("ToneMapping")
|
||||
property: "exposure"
|
||||
min: -10; max: 10
|
||||
}
|
||||
|
||||
Column {
|
||||
id: ambientOcclusion
|
||||
property var config: Render.getConfig("AmbientOcclusion")
|
||||
|
||||
Label { text: qsTr("Ambient Occlusion") }
|
||||
// TODO: Add gpuTimer
|
||||
CheckBox { text: qsTr("Dithering"); checked: ambientOcclusion.config.ditheringEnabled }
|
||||
Repeater {
|
||||
model: [
|
||||
"Resolution Level:resolutionLevel:4",
|
||||
"Obscurance Level:obscuranceLevel:1",
|
||||
"Radius:radius:2",
|
||||
"Falloff Bias:falloffBias:0.2",
|
||||
"Edge Sharpness:edgeSharpness:1",
|
||||
"Blur Radius:blurRadius:6",
|
||||
"Blur Deviation:blurDeviation:3"
|
||||
]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
config: ambientOcclusion.config
|
||||
property: modelData.split(":")[1]
|
||||
max: modelData.split(":")[2]
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
model: [
|
||||
"Samples:numSamples:32",
|
||||
"Spiral Turns:numSpiralTurns:30:"
|
||||
]
|
||||
ConfigSlider {
|
||||
label: qsTr(modelData.split(":")[0])
|
||||
integral: true
|
||||
config: ambientOcclusion.config
|
||||
property: modelData.split(":")[1]
|
||||
max: modelData.split(":")[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: debug
|
||||
property var config: Render.getConfig("DebugDeferredBuffer")
|
||||
|
||||
function setDebugMode(mode) {
|
||||
debug.config.enabled = (mode != 0);
|
||||
debug.config.mode = mode;
|
||||
}
|
||||
|
||||
Label { text: qsTr("Debug Buffer") }
|
||||
ExclusiveGroup { id: bufferGroup }
|
||||
Repeater {
|
||||
model: [
|
||||
"Off", "Diffuse", "Metallic", "Roughness", "Normal", "Depth",
|
||||
"Lighting", "Shadow", "Pyramid Depth", "Ambient Occlusion", "Custom Shader"
|
||||
]
|
||||
RadioButton {
|
||||
text: qsTr(modelData)
|
||||
exclusiveGroup: bufferGroup
|
||||
checked: index == 0
|
||||
onCheckedChanged: if (checked) debug.setDebugMode(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
//
|
||||
// renderEngineDebug.js
|
||||
// examples/utilities/tools
|
||||
//
|
||||
// Sam Gateau
|
||||
// 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
|
||||
//
|
||||
|
||||
Script.include("cookies.js");
|
||||
|
||||
var MENU = "Developer>Render>Debug Deferred Buffer";
|
||||
var ACTIONS = ["Off", "Diffuse", "Metallic", "Roughness", "Normal", "Depth", "Lighting", "Shadow", "PyramidDepth", "AmbientOcclusion", "OcclusionBlurred", "Custom"];
|
||||
var SETTINGS_KEY = "EngineDebugScript.DebugMode";
|
||||
|
||||
Number.prototype.clamp = function(min, max) {
|
||||
return Math.min(Math.max(this, min), max);
|
||||
};
|
||||
|
||||
var panel = new Panel(10, 100);
|
||||
|
||||
function CounterWidget(parentPanel, name, counter) {
|
||||
var subPanel = parentPanel.newSubPanel(name);
|
||||
var widget = parentPanel.items[name];
|
||||
widget.editTitle({ width: 270 });
|
||||
|
||||
subPanel.newSlider('Max Drawn', -1, 1,
|
||||
function(value) { counter.maxDrawn = value; }, // setter
|
||||
function() { return counter.maxDrawn; }, // getter
|
||||
function(value) { return value; });
|
||||
|
||||
var slider = subPanel.getWidget('Max Drawn');
|
||||
|
||||
this.update = function () {
|
||||
var numDrawn = counter.numDrawn; // avoid double polling
|
||||
var numMax = Math.max(numDrawn, 1);
|
||||
var title = [
|
||||
' ' + name,
|
||||
numDrawn + ' / ' + counter.numFeed
|
||||
].join('\t');
|
||||
|
||||
widget.editTitle({ text: title });
|
||||
slider.setMaxValue(numMax);
|
||||
};
|
||||
};
|
||||
|
||||
var opaquesCounter = new CounterWidget(panel, "Opaques", Render.opaque);
|
||||
var transparentsCounter = new CounterWidget(panel, "Transparents", Render.transparent);
|
||||
var overlaysCounter = new CounterWidget(panel, "Overlays", Render.overlay3D);
|
||||
|
||||
var resizing = false;
|
||||
var previousMode = Settings.getValue(SETTINGS_KEY, -1);
|
||||
previousMode = 8;
|
||||
Menu.addActionGroup(MENU, ACTIONS, ACTIONS[previousMode + 1]);
|
||||
Render.deferredDebugMode = previousMode;
|
||||
Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 }; // Reset to default size
|
||||
|
||||
function setEngineDeferredDebugSize(eventX) {
|
||||
var scaledX = (2.0 * (eventX / Window.innerWidth) - 1.0).clamp(-1.0, 1.0);
|
||||
Render.deferredDebugSize = { x: scaledX, y: -1.0, z: 1.0, w: 1.0 };
|
||||
}
|
||||
function shouldStartResizing(eventX) {
|
||||
var x = Math.abs(eventX - Window.innerWidth * (1.0 + Render.deferredDebugSize.x) / 2.0);
|
||||
var mode = Render.deferredDebugMode;
|
||||
return mode !== -1 && x < 20;
|
||||
}
|
||||
|
||||
function menuItemEvent(menuItem) {
|
||||
var index = ACTIONS.indexOf(menuItem);
|
||||
if (index >= 0) {
|
||||
Render.deferredDebugMode = (index - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// see libraries/render/src/render/Engine.h
|
||||
var showDisplayStatusFlag = 1;
|
||||
var showNetworkStatusFlag = 2;
|
||||
|
||||
panel.newCheckbox("Display status",
|
||||
function(value) { Render.displayItemStatus = (value ?
|
||||
Render.displayItemStatus | showDisplayStatusFlag :
|
||||
Render.displayItemStatus & ~showDisplayStatusFlag); },
|
||||
function() { return (Render.displayItemStatus & showDisplayStatusFlag) > 0; },
|
||||
function(value) { return (value & showDisplayStatusFlag) > 0; }
|
||||
);
|
||||
|
||||
panel.newCheckbox("Network/Physics status",
|
||||
function(value) { Render.displayItemStatus = (value ?
|
||||
Render.displayItemStatus | showNetworkStatusFlag :
|
||||
Render.displayItemStatus & ~showNetworkStatusFlag); },
|
||||
function() { return (Render.displayItemStatus & showNetworkStatusFlag) > 0; },
|
||||
function(value) { return (value & showNetworkStatusFlag) > 0; }
|
||||
);
|
||||
|
||||
panel.newSlider("Tone Mapping Exposure", -10, 10,
|
||||
function (value) { Render.tone.exposure = value; },
|
||||
function() { return Render.tone.exposure; },
|
||||
function (value) { return (value); });
|
||||
|
||||
panel.newSlider("Ambient Occlusion Resolution Level", 0.0, 4.0,
|
||||
function (value) { Render.ambientOcclusion.resolutionLevel = value; },
|
||||
function() { return Render.ambientOcclusion.resolutionLevel; },
|
||||
function (value) { return (value); });
|
||||
|
||||
panel.newSlider("Ambient Occlusion Radius", 0.0, 2.0,
|
||||
function (value) { Render.ambientOcclusion.radius = value; },
|
||||
function() { return Render.ambientOcclusion.radius; },
|
||||
function (value) { return (value.toFixed(2)); });
|
||||
|
||||
panel.newSlider("Ambient Occlusion Level", 0.0, 1.0,
|
||||
function (value) { Render.ambientOcclusion.level = value; },
|
||||
function() { return Render.ambientOcclusion.level; },
|
||||
function (value) { return (value.toFixed(2)); });
|
||||
|
||||
panel.newSlider("Ambient Occlusion Num Samples", 1, 32,
|
||||
function (value) { Render.ambientOcclusion.numSamples = value; },
|
||||
function() { return Render.ambientOcclusion.numSamples; },
|
||||
function (value) { return (value); });
|
||||
|
||||
panel.newSlider("Ambient Occlusion Num Spiral Turns", 0.0, 30.0,
|
||||
function (value) { Render.ambientOcclusion.numSpiralTurns = value; },
|
||||
function() { return Render.ambientOcclusion.numSpiralTurns; },
|
||||
function (value) { return (value.toFixed(2)); });
|
||||
|
||||
panel.newCheckbox("Ambient Occlusion Dithering",
|
||||
function (value) { Render.ambientOcclusion.ditheringEnabled = value; },
|
||||
function() { return Render.ambientOcclusion.ditheringEnabled; },
|
||||
function (value) { return (value); });
|
||||
|
||||
panel.newSlider("Ambient Occlusion Falloff Bias", 0.0, 0.2,
|
||||
function (value) { Render.ambientOcclusion.falloffBias = value; },
|
||||
function() { return Render.ambientOcclusion.falloffBias; },
|
||||
function (value) { return (value.toFixed(2)); });
|
||||
|
||||
panel.newSlider("Ambient Occlusion Edge Sharpness", 0.0, 1.0,
|
||||
function (value) { Render.ambientOcclusion.edgeSharpness = value; },
|
||||
function() { return Render.ambientOcclusion.edgeSharpness; },
|
||||
function (value) { return (value.toFixed(2)); });
|
||||
|
||||
panel.newSlider("Ambient Occlusion Blur Radius", 0.0, 6.0,
|
||||
function (value) { Render.ambientOcclusion.blurRadius = value; },
|
||||
function() { return Render.ambientOcclusion.blurRadius; },
|
||||
function (value) { return (value); });
|
||||
|
||||
panel.newSlider("Ambient Occlusion Blur Deviation", 0.0, 3.0,
|
||||
function (value) { Render.ambientOcclusion.blurDeviation = value; },
|
||||
function() { return Render.ambientOcclusion.blurDeviation; },
|
||||
function (value) { return (value.toFixed(2)); });
|
||||
|
||||
|
||||
panel.newSlider("Ambient Occlusion GPU time", 0.0, 10.0,
|
||||
function (value) {},
|
||||
function() { return Render.ambientOcclusion.gpuTime; },
|
||||
function (value) { return (value.toFixed(2) + " ms"); });
|
||||
|
||||
|
||||
var tickTackPeriod = 500;
|
||||
|
||||
function updateCounters() {
|
||||
opaquesCounter.update();
|
||||
transparentsCounter.update();
|
||||
overlaysCounter.update();
|
||||
panel.update("Ambient Occlusion GPU time");
|
||||
}
|
||||
Script.setInterval(updateCounters, tickTackPeriod);
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (resizing) {
|
||||
setEngineDeferredDebugSize(event.x);
|
||||
} else {
|
||||
panel.mouseMoveEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (shouldStartResizing(event.x)) {
|
||||
resizing = true;
|
||||
} else {
|
||||
panel.mousePressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (resizing) {
|
||||
resizing = false;
|
||||
} else {
|
||||
panel.mouseReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
|
||||
function scriptEnding() {
|
||||
panel.destroy();
|
||||
Menu.removeActionGroup(MENU);
|
||||
// Reset
|
||||
Settings.setValue(SETTINGS_KEY, Render.deferredDebugMode);
|
||||
Render.deferredDebugMode = -1;
|
||||
Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 };
|
||||
Render.opaque.maxDrawn = -1;
|
||||
Render.transparent.maxDrawn = -1;
|
||||
Render.overlay3D.maxDrawn = -1;
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
||||
// Collapse items
|
||||
panel.mousePressEvent({ x: panel.x, y: panel.items["Overlays"].y});
|
||||
panel.mousePressEvent({ x: panel.x, y: panel.items["Transparents"].y});
|
||||
panel.mousePressEvent({ x: panel.x, y: panel.items["Opaques"].y});
|
|
@ -19,12 +19,40 @@ Windows.Window {
|
|||
property var channel;
|
||||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
property alias source: pageLoader.source
|
||||
|
||||
Loader {
|
||||
id: pageLoader
|
||||
objectName: "Loader"
|
||||
focus: true
|
||||
property var dialog: root
|
||||
property var source;
|
||||
property var component;
|
||||
property var dynamicContent;
|
||||
onSourceChanged: {
|
||||
if (dynamicContent) {
|
||||
dynamicContent.destroy();
|
||||
dynamicContent = null;
|
||||
}
|
||||
component = Qt.createComponent(source);
|
||||
console.log("Created component " + component + " from source " + source);
|
||||
}
|
||||
} // dialog
|
||||
|
||||
onComponentChanged: {
|
||||
console.log("Component changed to " + component)
|
||||
populate();
|
||||
}
|
||||
|
||||
function populate() {
|
||||
console.log("Populate called: dynamicContent " + dynamicContent + " component " + component);
|
||||
if (!dynamicContent && component) {
|
||||
if (component.status == Component.Error) {
|
||||
console.log("Error loading component:", component.errorString());
|
||||
} else if (component.status == Component.Ready) {
|
||||
console.log("Building dynamic content");
|
||||
dynamicContent = component.createObject(contentHolder);
|
||||
} else {
|
||||
console.log("Component not yet ready, connecting to status change");
|
||||
component.statusChanged.connect(populate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: contentHolder
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ Fadable {
|
|||
HifiConstants { id: hifi }
|
||||
// The Window size is the size of the content, while the frame
|
||||
// decorations can extend outside it.
|
||||
implicitHeight: content.height
|
||||
implicitWidth: content.width
|
||||
implicitHeight: content ? content.height : 0
|
||||
implicitWidth: content ? content.width : 0
|
||||
x: -1; y: -1
|
||||
enabled: visible
|
||||
|
||||
|
|
|
@ -3158,8 +3158,9 @@ void Application::update(float deltaTime) {
|
|||
PerformanceTimer perfTimer("havestChanges");
|
||||
if (_physicsEngine->hasOutgoingChanges()) {
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_entitySimulation.handleOutgoingChanges(_physicsEngine->getOutgoingChanges(), Physics::getSessionUUID());
|
||||
avatarManager->handleOutgoingChanges(_physicsEngine->getOutgoingChanges());
|
||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges();
|
||||
_entitySimulation.handleOutgoingChanges(outgoingChanges, Physics::getSessionUUID());
|
||||
avatarManager->handleOutgoingChanges(outgoingChanges);
|
||||
});
|
||||
|
||||
auto collisionEvents = _physicsEngine->getCollisionEvents();
|
||||
|
@ -3877,6 +3878,8 @@ void Application::clearDomainOctreeDetails() {
|
|||
qCDebug(interfaceapp) << "Clearing domain octree details...";
|
||||
// reset the environment so that we don't erroneously end up with multiple
|
||||
|
||||
_physicsEnabled = false;
|
||||
|
||||
// reset our node to stats and node to jurisdiction maps... since these must be changing...
|
||||
_entityServerJurisdictions.withWriteLock([&] {
|
||||
_entityServerJurisdictions.clear();
|
||||
|
|
|
@ -367,6 +367,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
|
|||
|
||||
//virtual
|
||||
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||
const float MAX_OVERLAY_DT = 1.0f / 30.0f; // what to clamp delta-time to in AnimInverseKinematics::overlay
|
||||
if (dt > MAX_OVERLAY_DT) {
|
||||
dt = MAX_OVERLAY_DT;
|
||||
}
|
||||
|
||||
if (_relativePoses.size() != underPoses.size()) {
|
||||
loadPoses(underPoses);
|
||||
} else {
|
||||
|
@ -463,7 +468,8 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
|
||||
// smooth transitions by relaxing _hipsOffset toward the new value
|
||||
const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f;
|
||||
_hipsOffset += (newHipsOffset - _hipsOffset) * (dt / HIPS_OFFSET_SLAVE_TIMESCALE);
|
||||
float tau = dt > HIPS_OFFSET_SLAVE_TIMESCALE ? 1.0f : dt / HIPS_OFFSET_SLAVE_TIMESCALE;
|
||||
_hipsOffset += (newHipsOffset - _hipsOffset) * tau;
|
||||
}
|
||||
}
|
||||
return _relativePoses;
|
||||
|
|
|
@ -27,8 +27,8 @@ namespace AudioConstants {
|
|||
const int NETWORK_FRAME_SAMPLES_STEREO = NETWORK_FRAME_BYTES_STEREO / sizeof(AudioSample);
|
||||
const int NETWORK_FRAME_BYTES_PER_CHANNEL = 512;
|
||||
const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample);
|
||||
const float NETWORK_FRAME_MSECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL
|
||||
/ (float)AudioConstants::SAMPLE_RATE) * 1000.0f;
|
||||
const float NETWORK_FRAME_SECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL / float(AudioConstants::SAMPLE_RATE));
|
||||
const float NETWORK_FRAME_MSECS = NETWORK_FRAME_SECS * 1000.0f;
|
||||
|
||||
// be careful with overflows when using this constant
|
||||
const int NETWORK_FRAME_USECS = static_cast<int>(NETWORK_FRAME_MSECS * 1000.0f);
|
||||
|
|
|
@ -41,8 +41,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemI
|
|||
return; // bail early
|
||||
}
|
||||
|
||||
assert(properties.getLastEdited() > 0);
|
||||
|
||||
QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0);
|
||||
|
||||
if (EntityItemProperties::encodeEntityEditPacket(type, modelID, properties, bufferOut)) {
|
||||
|
|
|
@ -659,10 +659,10 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
// but since we're using macros below we have to temporarily modify overwriteLocalData.
|
||||
bool oldOverwrite = overwriteLocalData;
|
||||
overwriteLocalData = overwriteLocalData && !weOwnSimulation;
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotationFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocityFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityFromNetwork);
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
||||
overwriteLocalData = oldOverwrite;
|
||||
}
|
||||
|
@ -885,83 +885,93 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (hasAngularVelocity()) {
|
||||
glm::vec3 angularVelocity = getAngularVelocity();
|
||||
if (hasLocalAngularVelocity()) {
|
||||
glm::vec3 localAngularVelocity = getLocalAngularVelocity();
|
||||
|
||||
// angular damping
|
||||
if (_angularDamping > 0.0f) {
|
||||
angularVelocity *= powf(1.0f - _angularDamping, timeElapsed);
|
||||
localAngularVelocity *= powf(1.0f - _angularDamping, timeElapsed);
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << " angularDamping :" << _angularDamping;
|
||||
qCDebug(entities) << " newAngularVelocity:" << angularVelocity;
|
||||
qCDebug(entities) << " newAngularVelocity:" << localAngularVelocity;
|
||||
#endif
|
||||
}
|
||||
|
||||
float angularSpeed = glm::length(angularVelocity);
|
||||
float angularSpeed = glm::length(localAngularVelocity);
|
||||
|
||||
const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec
|
||||
if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) {
|
||||
if (setFlags && angularSpeed > 0.0f) {
|
||||
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
||||
}
|
||||
angularVelocity = ENTITY_ITEM_ZERO_VEC3;
|
||||
localAngularVelocity = ENTITY_ITEM_ZERO_VEC3;
|
||||
} else {
|
||||
// for improved agreement with the way Bullet integrates rotations we use an approximation
|
||||
// and break the integration into bullet-sized substeps
|
||||
glm::quat rotation = getRotation();
|
||||
float dt = timeElapsed;
|
||||
while (dt > PHYSICS_ENGINE_FIXED_SUBSTEP) {
|
||||
glm::quat dQ = computeBulletRotationStep(angularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP);
|
||||
glm::quat dQ = computeBulletRotationStep(localAngularVelocity, PHYSICS_ENGINE_FIXED_SUBSTEP);
|
||||
rotation = glm::normalize(dQ * rotation);
|
||||
dt -= PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||
}
|
||||
// NOTE: this final partial substep can drift away from a real Bullet simulation however
|
||||
// it only becomes significant for rapidly rotating objects
|
||||
// (e.g. around PI/4 radians per substep, or 7.5 rotations/sec at 60 substeps/sec).
|
||||
glm::quat dQ = computeBulletRotationStep(angularVelocity, dt);
|
||||
glm::quat dQ = computeBulletRotationStep(localAngularVelocity, dt);
|
||||
rotation = glm::normalize(dQ * rotation);
|
||||
|
||||
setRotation(rotation);
|
||||
}
|
||||
|
||||
setAngularVelocity(angularVelocity);
|
||||
setLocalAngularVelocity(localAngularVelocity);
|
||||
}
|
||||
|
||||
if (hasVelocity()) {
|
||||
if (hasLocalVelocity()) {
|
||||
|
||||
// acceleration is in the global frame, so transform it into the local frame.
|
||||
// TODO: Move this into SpatiallyNestable.
|
||||
bool success;
|
||||
Transform transform = getParentTransform(success);
|
||||
glm::vec3 localAcceleration(glm::vec3::_null);
|
||||
if (success) {
|
||||
localAcceleration = glm::inverse(transform.getRotation()) * getAcceleration();
|
||||
} else {
|
||||
localAcceleration = getAcceleration();
|
||||
}
|
||||
|
||||
// linear damping
|
||||
glm::vec3 velocity = getVelocity();
|
||||
glm::vec3 localVelocity = getLocalVelocity();
|
||||
if (_damping > 0.0f) {
|
||||
velocity *= powf(1.0f - _damping, timeElapsed);
|
||||
localVelocity *= powf(1.0f - _damping, timeElapsed);
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << " damping:" << _damping;
|
||||
qCDebug(entities) << " velocity AFTER dampingResistance:" << velocity;
|
||||
qCDebug(entities) << " glm::length(velocity):" << glm::length(velocity);
|
||||
qCDebug(entities) << " velocity AFTER dampingResistance:" << localVelocity;
|
||||
qCDebug(entities) << " glm::length(velocity):" << glm::length(localVelocity);
|
||||
#endif
|
||||
}
|
||||
|
||||
// integrate position forward
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 newPosition = position + (velocity * timeElapsed);
|
||||
glm::vec3 localPosition = getLocalPosition();
|
||||
glm::vec3 newLocalPosition = localPosition + (localVelocity * timeElapsed) + 0.5f * localAcceleration * timeElapsed * timeElapsed;
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << " EntityItem::simulate()....";
|
||||
qCDebug(entities) << " timeElapsed:" << timeElapsed;
|
||||
qCDebug(entities) << " old AACube:" << getMaximumAACube();
|
||||
qCDebug(entities) << " old position:" << position;
|
||||
qCDebug(entities) << " old velocity:" << velocity;
|
||||
qCDebug(entities) << " old position:" << localPosition;
|
||||
qCDebug(entities) << " old velocity:" << localVelocity;
|
||||
qCDebug(entities) << " old getAABox:" << getAABox();
|
||||
qCDebug(entities) << " newPosition:" << newPosition;
|
||||
qCDebug(entities) << " glm::distance(newPosition, position):" << glm::distance(newPosition, position);
|
||||
qCDebug(entities) << " glm::distance(newPosition, position):" << glm::distance(newLocalPosition, localPosition);
|
||||
#endif
|
||||
|
||||
position = newPosition;
|
||||
localPosition = newLocalPosition;
|
||||
|
||||
// apply effective acceleration, which will be the same as gravity if the Entity isn't at rest.
|
||||
if (hasAcceleration()) {
|
||||
velocity += getAcceleration() * timeElapsed;
|
||||
}
|
||||
localVelocity += localAcceleration * timeElapsed;
|
||||
|
||||
float speed = glm::length(velocity);
|
||||
float speed = glm::length(localVelocity);
|
||||
const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec
|
||||
if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) {
|
||||
setVelocity(ENTITY_ITEM_ZERO_VEC3);
|
||||
|
@ -969,8 +979,8 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) {
|
|||
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
||||
}
|
||||
} else {
|
||||
setPosition(position);
|
||||
setVelocity(velocity);
|
||||
setLocalPosition(localPosition);
|
||||
setLocalVelocity(localVelocity);
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
|
@ -986,6 +996,10 @@ bool EntityItem::isMoving() const {
|
|||
return hasVelocity() || hasAngularVelocity();
|
||||
}
|
||||
|
||||
bool EntityItem::isMovingRelativeToParent() const {
|
||||
return hasLocalVelocity() || hasLocalAngularVelocity();
|
||||
}
|
||||
|
||||
EntityTreePointer EntityItem::getTree() const {
|
||||
EntityTreeElementPointer containingElement = getElement();
|
||||
EntityTreePointer tree = containingElement ? containingElement->getTree() : nullptr;
|
||||
|
@ -1343,9 +1357,6 @@ void EntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
}
|
||||
|
||||
void EntityItem::updatePosition(const glm::vec3& value) {
|
||||
if (shouldSuppressLocationEdits()) {
|
||||
return;
|
||||
}
|
||||
if (getLocalPosition() != value) {
|
||||
setLocalPosition(value);
|
||||
_dirtyFlags |= Simulation::DIRTY_POSITION;
|
||||
|
@ -1358,6 +1369,13 @@ void EntityItem::updatePosition(const glm::vec3& value) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItem::updatePositionFromNetwork(const glm::vec3& value) {
|
||||
if (shouldSuppressLocationEdits()) {
|
||||
return;
|
||||
}
|
||||
updatePosition(value);
|
||||
}
|
||||
|
||||
void EntityItem::updateDimensions(const glm::vec3& value) {
|
||||
if (getDimensions() != value) {
|
||||
setDimensions(value);
|
||||
|
@ -1366,9 +1384,6 @@ void EntityItem::updateDimensions(const glm::vec3& value) {
|
|||
}
|
||||
|
||||
void EntityItem::updateRotation(const glm::quat& rotation) {
|
||||
if (shouldSuppressLocationEdits()) {
|
||||
return;
|
||||
}
|
||||
if (getLocalOrientation() != rotation) {
|
||||
setLocalOrientation(rotation);
|
||||
_dirtyFlags |= Simulation::DIRTY_ROTATION;
|
||||
|
@ -1382,6 +1397,13 @@ void EntityItem::updateRotation(const glm::quat& rotation) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItem::updateRotationFromNetwork(const glm::quat& rotation) {
|
||||
if (shouldSuppressLocationEdits()) {
|
||||
return;
|
||||
}
|
||||
updateRotation(rotation);
|
||||
}
|
||||
|
||||
void EntityItem::updateMass(float mass) {
|
||||
// Setting the mass actually changes the _density (at fixed volume), however
|
||||
// we must protect the density range to help maintain stability of physics simulation
|
||||
|
@ -1406,9 +1428,6 @@ void EntityItem::updateMass(float mass) {
|
|||
}
|
||||
|
||||
void EntityItem::updateVelocity(const glm::vec3& value) {
|
||||
if (shouldSuppressLocationEdits()) {
|
||||
return;
|
||||
}
|
||||
glm::vec3 velocity = getLocalVelocity();
|
||||
if (velocity != value) {
|
||||
const float MIN_LINEAR_SPEED = 0.001f;
|
||||
|
@ -1422,6 +1441,13 @@ void EntityItem::updateVelocity(const glm::vec3& value) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItem::updateVelocityFromNetwork(const glm::vec3& value) {
|
||||
if (shouldSuppressLocationEdits()) {
|
||||
return;
|
||||
}
|
||||
updateVelocity(value);
|
||||
}
|
||||
|
||||
void EntityItem::updateDamping(float value) {
|
||||
auto clampedDamping = glm::clamp(value, 0.0f, 1.0f);
|
||||
if (_damping != clampedDamping) {
|
||||
|
@ -1438,9 +1464,6 @@ void EntityItem::updateGravity(const glm::vec3& value) {
|
|||
}
|
||||
|
||||
void EntityItem::updateAngularVelocity(const glm::vec3& value) {
|
||||
if (shouldSuppressLocationEdits()) {
|
||||
return;
|
||||
}
|
||||
glm::vec3 angularVelocity = getLocalAngularVelocity();
|
||||
if (angularVelocity != value) {
|
||||
const float MIN_ANGULAR_SPEED = 0.0002f;
|
||||
|
@ -1454,6 +1477,13 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItem::updateAngularVelocityFromNetwork(const glm::vec3& value) {
|
||||
if (shouldSuppressLocationEdits()) {
|
||||
return;
|
||||
}
|
||||
updateAngularVelocity(value);
|
||||
}
|
||||
|
||||
void EntityItem::updateAngularDamping(float value) {
|
||||
auto clampedDamping = glm::clamp(value, 0.0f, 1.0f);
|
||||
if (_angularDamping != clampedDamping) {
|
||||
|
|
|
@ -194,6 +194,7 @@ public:
|
|||
float getDensity() const { return _density; }
|
||||
|
||||
bool hasVelocity() const { return getVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
||||
bool hasLocalVelocity() const { return getLocalVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
||||
|
||||
const glm::vec3& getGravity() const { return _gravity; } /// get gravity in meters
|
||||
void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters
|
||||
|
@ -255,6 +256,7 @@ public:
|
|||
}
|
||||
|
||||
bool hasAngularVelocity() const { return getAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
||||
bool hasLocalAngularVelocity() const { return getLocalAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; }
|
||||
|
||||
float getAngularDamping() const { return _angularDamping; }
|
||||
void setAngularDamping(float value) { _angularDamping = value; }
|
||||
|
@ -318,16 +320,20 @@ public:
|
|||
|
||||
// updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags
|
||||
void updatePosition(const glm::vec3& value);
|
||||
void updatePositionFromNetwork(const glm::vec3& value);
|
||||
void updateDimensions(const glm::vec3& value);
|
||||
void updateRotation(const glm::quat& rotation);
|
||||
void updateRotationFromNetwork(const glm::quat& rotation);
|
||||
void updateDensity(float value);
|
||||
void updateMass(float value);
|
||||
void updateVelocity(const glm::vec3& value);
|
||||
void updateVelocityFromNetwork(const glm::vec3& value);
|
||||
void updateDamping(float value);
|
||||
void updateRestitution(float value);
|
||||
void updateFriction(float value);
|
||||
void updateGravity(const glm::vec3& value);
|
||||
void updateAngularVelocity(const glm::vec3& value);
|
||||
void updateAngularVelocityFromNetwork(const glm::vec3& value);
|
||||
void updateAngularDamping(float value);
|
||||
void updateCollisionless(bool value);
|
||||
void updateCollisionMask(uint8_t value);
|
||||
|
@ -340,6 +346,7 @@ public:
|
|||
void clearDirtyFlags(uint32_t mask = 0xffffffff) { _dirtyFlags &= ~mask; }
|
||||
|
||||
bool isMoving() const;
|
||||
bool isMovingRelativeToParent() const;
|
||||
|
||||
bool isSimulated() const { return _simulated; }
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
|||
SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin();
|
||||
while (itemItr != _simpleKinematicEntities.end()) {
|
||||
EntityItemPointer entity = *itemItr;
|
||||
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
||||
if (entity->isMovingRelativeToParent() && !entity->getPhysicsInfo()) {
|
||||
entity->simulate(now);
|
||||
_entitiesToSort.insert(entity);
|
||||
++itemItr;
|
||||
|
|
|
@ -313,7 +313,7 @@ OffscreenQmlSurface::OffscreenQmlSurface() {
|
|||
|
||||
OffscreenQmlSurface::~OffscreenQmlSurface() {
|
||||
_renderer->stop();
|
||||
|
||||
delete _rootItem;
|
||||
delete _renderer;
|
||||
delete _qmlComponent;
|
||||
delete _qmlEngine;
|
||||
|
|
|
@ -167,11 +167,13 @@ void PhysicalEntitySimulation::getObjectsToRemoveFromPhysics(VectorOfMotionState
|
|||
_entitiesToAddToPhysics.remove(entity);
|
||||
|
||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||
assert(motionState);
|
||||
_pendingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
result.push_back(motionState);
|
||||
_entitiesToRelease.insert(entity);
|
||||
if (motionState) {
|
||||
_pendingChanges.remove(motionState);
|
||||
_outgoingChanges.remove(motionState);
|
||||
_physicalObjects.remove(motionState);
|
||||
result.push_back(motionState);
|
||||
_entitiesToRelease.insert(entity);
|
||||
}
|
||||
|
||||
if (entity->isSimulated() && entity->isDead()) {
|
||||
_entitiesToDelete.insert(entity);
|
||||
|
|
|
@ -136,23 +136,19 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
motionState->clearIncomingDirtyFlags();
|
||||
}
|
||||
|
||||
// private
|
||||
void PhysicsEngine::removeObjectFromDynamicsWorld(ObjectMotionState* object) {
|
||||
// wake up anything touching this object
|
||||
bump(object);
|
||||
removeContacts(object);
|
||||
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
assert(body);
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
||||
// first bump and prune contacts for all objects in the list
|
||||
for (auto object : objects) {
|
||||
removeObjectFromDynamicsWorld(object);
|
||||
bumpAndPruneContacts(object);
|
||||
}
|
||||
|
||||
// then remove them
|
||||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
assert(body);
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
object->setRigidBody(nullptr);
|
||||
body->setMotionState(nullptr);
|
||||
delete body;
|
||||
|
@ -163,7 +159,8 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
|
|||
void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) {
|
||||
for (auto object : objects) {
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
removeObjectFromDynamicsWorld(object);
|
||||
assert(body);
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
|
||||
// NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it.
|
||||
object->setRigidBody(nullptr);
|
||||
|
@ -200,7 +197,13 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob
|
|||
}
|
||||
|
||||
void PhysicsEngine::reinsertObject(ObjectMotionState* object) {
|
||||
removeObjectFromDynamicsWorld(object);
|
||||
// remove object from DynamicsWorld
|
||||
bumpAndPruneContacts(object);
|
||||
btRigidBody* body = object->getRigidBody();
|
||||
assert(body);
|
||||
_dynamicsWorld->removeRigidBody(body);
|
||||
|
||||
// add it back
|
||||
addObjectToDynamicsWorld(object);
|
||||
}
|
||||
|
||||
|
@ -402,7 +405,7 @@ void PhysicsEngine::dumpStatsIfNecessary() {
|
|||
// CF_DISABLE_VISUALIZE_OBJECT = 32, //disable debug drawing
|
||||
// CF_DISABLE_SPU_COLLISION_PROCESSING = 64//disable parallel/SPU processing
|
||||
|
||||
void PhysicsEngine::bump(ObjectMotionState* motionState) {
|
||||
void PhysicsEngine::bumpAndPruneContacts(ObjectMotionState* motionState) {
|
||||
// Find all objects that touch the object corresponding to motionState and flag the other objects
|
||||
// for simulation ownership by the local simulation.
|
||||
|
||||
|
@ -434,6 +437,7 @@ void PhysicsEngine::bump(ObjectMotionState* motionState) {
|
|||
}
|
||||
}
|
||||
}
|
||||
removeContacts(motionState);
|
||||
}
|
||||
|
||||
void PhysicsEngine::setCharacterController(CharacterController* character) {
|
||||
|
|
|
@ -78,9 +78,6 @@ public:
|
|||
/// \return position of simulation origin in domain-frame
|
||||
const glm::vec3& getOriginOffset() const { return _originOffset; }
|
||||
|
||||
/// \brief call bump on any objects that touch the object corresponding to motionState
|
||||
void bump(ObjectMotionState* motionState);
|
||||
|
||||
void setCharacterController(CharacterController* character);
|
||||
|
||||
void dumpNextStats() { _dumpNextStats = true; }
|
||||
|
@ -94,7 +91,9 @@ public:
|
|||
|
||||
private:
|
||||
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
||||
void removeObjectFromDynamicsWorld(ObjectMotionState* motionState);
|
||||
|
||||
/// \brief bump any objects that touch this one, then remove contact info
|
||||
void bumpAndPruneContacts(ObjectMotionState* motionState);
|
||||
|
||||
void removeContacts(ObjectMotionState* motionState);
|
||||
|
||||
|
|
|
@ -115,8 +115,8 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) {
|
|||
}
|
||||
|
||||
void ThreadSafeDynamicsWorld::synchronizeMotionStates() {
|
||||
_changedMotionStates.clear();
|
||||
BT_PROFILE("synchronizeMotionStates");
|
||||
_changedMotionStates.clear();
|
||||
if (m_synchronizeAllMotionStates) {
|
||||
//iterate over all collision objects
|
||||
for (int i=0;i<m_collisionObjects.size();i++) {
|
||||
|
|
|
@ -40,14 +40,14 @@ public:
|
|||
int stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps = 1,
|
||||
btScalar fixedTimeStep = btScalar(1.)/btScalar(60.),
|
||||
SubStepCallback onSubStep = []() { });
|
||||
void synchronizeMotionStates();
|
||||
virtual void synchronizeMotionStates() override;
|
||||
|
||||
// btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated
|
||||
// but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide
|
||||
// smoother rendering of objects when the physics simulation loop is ansynchronous to the render loop).
|
||||
float getLocalTimeAccumulation() const { return m_localTime; }
|
||||
|
||||
VectorOfMotionStates& getChangedMotionStates() { return _changedMotionStates; }
|
||||
const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; }
|
||||
|
||||
private:
|
||||
// call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState()
|
||||
|
|
|
@ -166,7 +166,8 @@ void DrawDeferred::run(const SceneContextPointer& sceneContext, const RenderCont
|
|||
batch.setStateScissorRect(args->_viewport);
|
||||
args->_batch = &batch;
|
||||
|
||||
config->numDrawn = (int)inItems.size();
|
||||
config->setNumDrawn((int)inItems.size());
|
||||
emit config->numDrawnChanged();
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
|
@ -203,7 +204,8 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
|
|||
inItems.emplace_back(id);
|
||||
}
|
||||
}
|
||||
config->numItems = (int)inItems.size();
|
||||
config->setNumDrawn((int)inItems.size());
|
||||
emit config->numDrawnChanged();
|
||||
|
||||
if (!inItems.empty()) {
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
|
|
@ -40,16 +40,21 @@ public:
|
|||
|
||||
class DrawConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int numDrawn READ getNumDrawn)
|
||||
Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged)
|
||||
Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty)
|
||||
public:
|
||||
|
||||
int getNumDrawn() { return numDrawn; }
|
||||
void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); }
|
||||
|
||||
int numDrawn{ 0 };
|
||||
int maxDrawn{ -1 };
|
||||
|
||||
signals:
|
||||
void numDrawnChanged();
|
||||
void dirty();
|
||||
|
||||
protected:
|
||||
int numDrawn{ 0 };
|
||||
};
|
||||
|
||||
class DrawDeferred {
|
||||
|
@ -87,15 +92,20 @@ public:
|
|||
|
||||
class DrawOverlay3DConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int numItems READ getNumItems)
|
||||
Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged)
|
||||
Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty)
|
||||
public:
|
||||
int getNumItems() { return numItems; }
|
||||
int getNumDrawn() { return numDrawn; }
|
||||
void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); }
|
||||
|
||||
int numItems{ 0 };
|
||||
int maxDrawn{ -1 };
|
||||
|
||||
signals:
|
||||
void numDrawnChanged();
|
||||
void dirty();
|
||||
|
||||
protected:
|
||||
int numDrawn{ 0 };
|
||||
};
|
||||
|
||||
class DrawOverlay3D {
|
||||
|
|
|
@ -71,10 +71,12 @@ public:
|
|||
bool alwaysEnabled{ true };
|
||||
bool enabled{ true };
|
||||
|
||||
// This must be named toJSON to integrate with the global scripting JSON object
|
||||
Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); }
|
||||
Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); }
|
||||
|
||||
public slots:
|
||||
void load(const QJsonValue& json) { qObjectFromJsonValue(json, *this); }
|
||||
void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); }
|
||||
};
|
||||
|
||||
class TaskConfig : public JobConfig {
|
||||
|
@ -85,6 +87,9 @@ public:
|
|||
|
||||
void init(Task* task) { _task = task; }
|
||||
|
||||
// getter for qml integration, prefer the templated getter
|
||||
Q_INVOKABLE QObject* getConfig(const QString& name) { return QObject::findChild<JobConfig*>(name); }
|
||||
// getter for cpp (strictly typed), prefer this getter
|
||||
template <class T> typename T::Config* getConfig(std::string job = "") const {
|
||||
QString name = job.empty() ? QString() : QString(job.c_str()); // an empty string is not a null string
|
||||
return findChild<typename T::Config*>(name);
|
||||
|
|
|
@ -416,7 +416,7 @@ void SpatiallyNestable::setVelocity(const glm::vec3& velocity, bool& success) {
|
|||
Transform parentTransform = getParentTransform(success);
|
||||
_velocityLock.withWriteLock([&] {
|
||||
// TODO: take parent angularVelocity into account.
|
||||
_velocity = glm::inverse(parentTransform.getRotation()) * velocity - parentVelocity;
|
||||
_velocity = glm::inverse(parentTransform.getRotation()) * (velocity - parentVelocity);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -460,7 +460,7 @@ void SpatiallyNestable::setAngularVelocity(const glm::vec3& angularVelocity, boo
|
|||
glm::vec3 parentAngularVelocity = getParentAngularVelocity(success);
|
||||
Transform parentTransform = getParentTransform(success);
|
||||
_angularVelocityLock.withWriteLock([&] {
|
||||
_angularVelocity = glm::inverse(parentTransform.getRotation()) * angularVelocity - parentAngularVelocity;
|
||||
_angularVelocity = glm::inverse(parentTransform.getRotation()) * (angularVelocity - parentAngularVelocity);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -111,10 +111,9 @@ void qObjectFromJsonValue(const QJsonValue& j, QObject& o) {
|
|||
for (auto it = object.begin(); it != object.end(); it++) {
|
||||
std::string key = it.key().toStdString();
|
||||
if (it.value().isObject()) {
|
||||
QVariant child = o.property(key.c_str());
|
||||
if (child.isValid()) {
|
||||
QObject* object = child.value<QObject*>();
|
||||
qObjectFromJsonValue(it.value(), *object);
|
||||
QObject* child = o.findChild<QObject*>(key.c_str(), Qt::FindChildOption::FindDirectChildrenOnly);
|
||||
if (child) {
|
||||
qObjectFromJsonValue(it.value(), *child);
|
||||
}
|
||||
} else {
|
||||
o.setProperty(key.c_str(), it.value());
|
||||
|
|
|
@ -39,6 +39,7 @@ glm::mat4 OculusBaseDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
|
|||
|
||||
bool OculusBaseDisplayPlugin::isSupported() const {
|
||||
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
|
||||
qDebug() << "OculusBaseDisplayPlugin : ovr_Initialize() failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -48,6 +49,7 @@ bool OculusBaseDisplayPlugin::isSupported() const {
|
|||
if (!OVR_SUCCESS(result)) {
|
||||
ovrErrorInfo error;
|
||||
ovr_GetLastErrorInfo(&error);
|
||||
qDebug() << "OculusBaseDisplayPlugin : ovr_Create() failed" << result << error.Result << error.ErrorString;
|
||||
ovr_Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -6,20 +6,17 @@
|
|||
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
# OpenVR is disabled until a) it works with threaded present and
|
||||
# b) it doesn't interfere with Oculus SDK 0.8
|
||||
if (FALSE)
|
||||
#if (WIN32)
|
||||
if (WIN32)
|
||||
# we're using static GLEW, so define GLEW_STATIC
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
set(TARGET_NAME openvr)
|
||||
setup_hifi_plugin(OpenGL Script Qml Widgets)
|
||||
link_hifi_libraries(shared gl networking controllers
|
||||
plugins display-plugins input-plugins script-engine
|
||||
link_hifi_libraries(shared gl networking controllers
|
||||
plugins display-plugins input-plugins script-engine
|
||||
render-utils model gpu render model-networking fbx)
|
||||
|
||||
include_hifi_library_headers(octree)
|
||||
|
||||
|
||||
add_dependency_external_projects(OpenVR)
|
||||
find_package(OpenVR REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
|
||||
|
|
|
@ -32,12 +32,15 @@ const QString OpenVrDisplayPlugin::NAME("OpenVR (Vive)");
|
|||
const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; // this probably shouldn't be hardcoded here
|
||||
|
||||
static vr::IVRCompositor* _compositor{ nullptr };
|
||||
static vr::TrackedDevicePose_t _presentThreadTrackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||
vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount];
|
||||
mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount];
|
||||
static mat4 _sensorResetMat;
|
||||
static uvec2 _windowSize;
|
||||
static uvec2 _renderTargetSize;
|
||||
|
||||
|
||||
|
||||
struct PerEyeData {
|
||||
//uvec2 _viewportOrigin;
|
||||
//uvec2 _viewportSize;
|
||||
|
@ -69,10 +72,7 @@ mat4 toGlm(const vr::HmdMatrix34_t& m) {
|
|||
}
|
||||
|
||||
bool OpenVrDisplayPlugin::isSupported() const {
|
||||
auto hmd = acquireOpenVrSystem();
|
||||
bool success = nullptr != hmd;
|
||||
releaseOpenVrSystem();
|
||||
return success;
|
||||
return vr::VR_IsHmdPresent();
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::activate() {
|
||||
|
@ -87,11 +87,16 @@ void OpenVrDisplayPlugin::activate() {
|
|||
// Recommended render target size is per-eye, so double the X size for
|
||||
// left + right eyes
|
||||
_renderTargetSize.x *= 2;
|
||||
openvr_for_each_eye([&](vr::Hmd_Eye eye) {
|
||||
PerEyeData& eyeData = _eyesData[eye];
|
||||
eyeData._projectionMatrix = toGlm(_hmd->GetProjectionMatrix(eye, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, vr::API_OpenGL));
|
||||
eyeData._eyeOffset = toGlm(_hmd->GetEyeToHeadTransform(eye));
|
||||
});
|
||||
|
||||
{
|
||||
Lock lock(_poseMutex);
|
||||
openvr_for_each_eye([&](vr::Hmd_Eye eye) {
|
||||
PerEyeData& eyeData = _eyesData[eye];
|
||||
eyeData._projectionMatrix = toGlm(_hmd->GetProjectionMatrix(eye, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, vr::API_OpenGL));
|
||||
eyeData._eyeOffset = toGlm(_hmd->GetEyeToHeadTransform(eye));
|
||||
});
|
||||
}
|
||||
|
||||
_compositor = vr::VRCompositor();
|
||||
Q_ASSERT(_compositor);
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
|
@ -115,6 +120,10 @@ void OpenVrDisplayPlugin::customizeContext() {
|
|||
glGetError();
|
||||
});
|
||||
WindowOpenGLDisplayPlugin::customizeContext();
|
||||
|
||||
enableVsync(false);
|
||||
// Only enable mirroring if we know vsync is disabled
|
||||
_enablePreview = !isVsyncEnabled();
|
||||
}
|
||||
|
||||
uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const {
|
||||
|
@ -126,25 +135,24 @@ mat4 OpenVrDisplayPlugin::getProjection(Eye eye, const mat4& baseProjection) con
|
|||
if (eye == Mono) {
|
||||
eye = Left;
|
||||
}
|
||||
Lock lock(_poseMutex);
|
||||
return _eyesData[eye]._projectionMatrix;
|
||||
}
|
||||
|
||||
void OpenVrDisplayPlugin::resetSensors() {
|
||||
_sensorResetMat = glm::inverse(cancelOutRollAndPitch(_trackedDevicePoseMat4[0]));
|
||||
Lock lock(_poseMutex);
|
||||
glm::mat4 m = toGlm(_trackedDevicePose[0].mDeviceToAbsoluteTracking);
|
||||
_sensorResetMat = glm::inverse(cancelOutRollAndPitch(m));
|
||||
}
|
||||
|
||||
glm::mat4 OpenVrDisplayPlugin::getEyeToHeadTransform(Eye eye) const {
|
||||
Lock lock(_poseMutex);
|
||||
return _eyesData[eye]._eyeOffset;
|
||||
}
|
||||
|
||||
glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const {
|
||||
glm::mat4 result;
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
result = _trackedDevicePoseMat4[0];
|
||||
|
||||
}
|
||||
return result;
|
||||
Lock lock(_poseMutex);
|
||||
return _trackedDevicePoseMat4[0];
|
||||
}
|
||||
|
||||
|
||||
|
@ -156,17 +164,40 @@ void OpenVrDisplayPlugin::internalPresent() {
|
|||
// Flip y-axis since GL UV coords are backwards.
|
||||
static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 };
|
||||
static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 };
|
||||
vr::Texture_t texture{ (void*)_currentSceneTexture, vr::API_OpenGL, vr::ColorSpace_Auto };
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
|
||||
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
|
||||
|
||||
// screen preview mirroring
|
||||
if (_enablePreview) {
|
||||
auto windowSize = toGlm(_window->size());
|
||||
if (_monoPreview) {
|
||||
glViewport(0, 0, windowSize.x * 2, windowSize.y);
|
||||
glScissor(0, windowSize.y, windowSize.x, windowSize.y);
|
||||
} else {
|
||||
glViewport(0, 0, windowSize.x, windowSize.y);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, _currentSceneTexture);
|
||||
GLenum err = glGetError();
|
||||
Q_ASSERT(0 == err);
|
||||
drawUnitQuad();
|
||||
}
|
||||
|
||||
vr::Texture_t texture{ (void*)_currentSceneTexture, vr::API_OpenGL, vr::ColorSpace_Auto };
|
||||
|
||||
_compositor->Submit(vr::Eye_Left, &texture, &leftBounds);
|
||||
_compositor->Submit(vr::Eye_Right, &texture, &rightBounds);
|
||||
|
||||
glFinish();
|
||||
|
||||
if (_enablePreview) {
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
_compositor->WaitGetPoses(_presentThreadTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
||||
|
||||
{
|
||||
Lock lock(_mutex);
|
||||
_compositor->WaitGetPoses(_trackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
||||
// copy and process _presentThreadTrackedDevicePoses
|
||||
Lock lock(_poseMutex);
|
||||
for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) {
|
||||
_trackedDevicePose[i] = _presentThreadTrackedDevicePose[i];
|
||||
_trackedDevicePoseMat4[i] = _sensorResetMat * toGlm(_trackedDevicePose[i].mDeviceToAbsoluteTracking);
|
||||
}
|
||||
openvr_for_each_eye([&](vr::Hmd_Eye eye) {
|
||||
|
|
|
@ -45,5 +45,8 @@ protected:
|
|||
private:
|
||||
vr::IVRSystem* _hmd { nullptr };
|
||||
static const QString NAME;
|
||||
bool _enablePreview { false };
|
||||
bool _monoPreview { true };
|
||||
mutable Mutex _poseMutex;
|
||||
};
|
||||
|
||||
|
|
|
@ -23,11 +23,11 @@ using Lock = std::unique_lock<Mutex>;
|
|||
static int refCount { 0 };
|
||||
static Mutex mutex;
|
||||
static vr::IVRSystem* activeHmd { nullptr };
|
||||
static bool hmdPresent = vr::VR_IsHmdPresent();
|
||||
|
||||
static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000;
|
||||
|
||||
vr::IVRSystem* acquireOpenVrSystem() {
|
||||
bool hmdPresent = vr::VR_IsHmdPresent();
|
||||
if (hmdPresent) {
|
||||
Lock lock(mutex);
|
||||
if (!activeHmd) {
|
||||
|
@ -40,6 +40,8 @@ vr::IVRSystem* acquireOpenVrSystem() {
|
|||
qCDebug(displayplugins) << "openvr: incrementing refcount";
|
||||
++refCount;
|
||||
}
|
||||
} else {
|
||||
qCDebug(displayplugins) << "openvr: no hmd present";
|
||||
}
|
||||
return activeHmd;
|
||||
}
|
||||
|
@ -51,24 +53,7 @@ void releaseOpenVrSystem() {
|
|||
--refCount;
|
||||
if (0 == refCount) {
|
||||
qCDebug(displayplugins) << "openvr: zero refcount, deallocate VR system";
|
||||
// Avoid spamming the VR system with activate/deactivate calls at system startup by
|
||||
// putting in a delay before we destory the shutdown the VR subsystem
|
||||
|
||||
// FIXME releasing the VR system at all seems to trigger an exception deep inside the Oculus DLL.
|
||||
// disabling for now.
|
||||
//QTimer* releaseTimer = new QTimer();
|
||||
//releaseTimer->singleShot(RELEASE_OPENVR_HMD_DELAY_MS, [releaseTimer] {
|
||||
// Lock lock(mutex);
|
||||
// qDebug() << "Delayed openvr destroy activated";
|
||||
// if (0 == refCount && nullptr != activeHmd) {
|
||||
// qDebug() << "Delayed openvr destroy: releasing resources";
|
||||
// activeHmd = nullptr;
|
||||
// vr::VR_Shutdown();
|
||||
// } else {
|
||||
// qDebug() << "Delayed openvr destroy: HMD still in use";
|
||||
// }
|
||||
// releaseTimer->deleteLater();
|
||||
//});
|
||||
vr::VR_Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,10 +48,7 @@ static const QString RENDER_CONTROLLERS = "Render Hand Controllers";
|
|||
const QString ViveControllerManager::NAME = "OpenVR";
|
||||
|
||||
bool ViveControllerManager::isSupported() const {
|
||||
auto hmd = acquireOpenVrSystem();
|
||||
bool success = hmd != nullptr;
|
||||
releaseOpenVrSystem();
|
||||
return success;
|
||||
return vr::VR_IsHmdPresent();
|
||||
}
|
||||
|
||||
void ViveControllerManager::activate() {
|
||||
|
@ -66,10 +63,12 @@ void ViveControllerManager::activate() {
|
|||
}
|
||||
Q_ASSERT(_hmd);
|
||||
|
||||
// OpenVR provides 3d mesh representations of the controllers
|
||||
// Disabled controller rendering code
|
||||
/*
|
||||
auto renderModels = vr::VRRenderModels();
|
||||
|
||||
vr::RenderModel_t model;
|
||||
/*
|
||||
if (!_hmd->LoadRenderModel(CONTROLLER_MODEL_STRING, &model)) {
|
||||
qDebug() << QString("Unable to load render model %1\n").arg(CONTROLLER_MODEL_STRING);
|
||||
} else {
|
||||
|
@ -145,6 +144,7 @@ void ViveControllerManager::deactivate() {
|
|||
void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges) {
|
||||
PerformanceTimer perfTimer("ViveControllerManager::updateRendering");
|
||||
|
||||
/*
|
||||
if (_modelLoaded) {
|
||||
//auto controllerPayload = new render::Payload<ViveControllerManager>(this);
|
||||
//auto controllerPayloadPointer = ViveControllerManager::PayloadPointer(controllerPayload);
|
||||
|
@ -175,9 +175,11 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint
|
|||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign) {
|
||||
/*
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
Transform transform(userInputMapper->getSensorToWorldMat());
|
||||
transform.postTranslate(pose.getTranslation() + pose.getRotation() * glm::vec3(0, 0, CONTROLLER_LENGTH_OFFSET));
|
||||
|
@ -199,6 +201,7 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch&
|
|||
// mesh->getVertexBuffer()._stride);
|
||||
batch.setIndexBuffer(gpu::UINT16, mesh->getIndexBuffer()._buffer, 0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ enable_testing()
|
|||
|
||||
# add the test directories
|
||||
file(GLOB TEST_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*")
|
||||
list(REMOVE_ITEM TEST_SUBDIRS "CMakeFiles")
|
||||
list(REMOVE_ITEM TEST_SUBDIRS "CMakeFiles" "mocha")
|
||||
foreach(DIR ${TEST_SUBDIRS})
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}")
|
||||
set(TEST_PROJ_NAME ${DIR})
|
||||
|
|
5
tests/mocha/README.md
Normal file
5
tests/mocha/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
mocha tests of javascript code (e.g., from ../../examples/libraries/).
|
||||
```
|
||||
npm install
|
||||
npm test
|
||||
```
|
11
tests/mocha/package.json
Normal file
11
tests/mocha/package.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "HighFidelityTests",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
"mocha": "^2.2.1"
|
||||
}
|
||||
}
|
248
tests/mocha/test/testVirtualBaton.js
Normal file
248
tests/mocha/test/testVirtualBaton.js
Normal file
|
@ -0,0 +1,248 @@
|
|||
"use strict";
|
||||
/*jslint nomen: true, plusplus: true, vars: true */
|
||||
var assert = require('assert');
|
||||
var mocha = require('mocha'), describe = mocha.describe, it = mocha.it, after = mocha.after;
|
||||
var virtualBaton = require('../../../examples/libraries/virtualBaton.js');
|
||||
|
||||
describe('temp', function () {
|
||||
var messageCount = 0, testStart = Date.now();
|
||||
function makeMessager(nodes, me, mode) { // shim for High Fidelity Message system
|
||||
function noopSend(channel, string, source) {
|
||||
}
|
||||
function hasChannel(node, channel) {
|
||||
return -1 !== node.subscribed.indexOf(channel);
|
||||
}
|
||||
function sendSync(channel, message, nodes, skip) {
|
||||
nodes.forEach(function (node) {
|
||||
if (!hasChannel(node, channel) || (node === skip)) {
|
||||
return;
|
||||
}
|
||||
node.sender(channel, message, me.name);
|
||||
});
|
||||
}
|
||||
nodes.forEach(function (node) {
|
||||
node.sender = node.sender || noopSend;
|
||||
node.subscribed = node.subscribed || [];
|
||||
});
|
||||
return {
|
||||
subscriberCount: function () {
|
||||
var c = 0;
|
||||
nodes.forEach(function (n) {
|
||||
if (n.subscribed.length) {
|
||||
c++;
|
||||
}
|
||||
});
|
||||
return c;
|
||||
},
|
||||
subscribe: function (channel) {
|
||||
me.subscribed.push(channel);
|
||||
},
|
||||
unsubscribe: function (channel) {
|
||||
me.subscribed.splice(me.subscribed.indexOf(channel), 1);
|
||||
},
|
||||
sendMessage: function (channel, message) {
|
||||
if ((mode === 'immediate2Me') && hasChannel(me, channel)) {
|
||||
me.sender(channel, message, me.name);
|
||||
}
|
||||
if (mode === 'immediate') {
|
||||
sendSync(channel, message, nodes, null);
|
||||
} else {
|
||||
process.nextTick(function () {
|
||||
sendSync(channel, message, nodes, (mode === 'immediate2Me') ? me : null);
|
||||
});
|
||||
}
|
||||
},
|
||||
messageReceived: {
|
||||
connect: function (f) {
|
||||
me.sender = function (c, m, i) {
|
||||
messageCount++; f(c, m, i);
|
||||
};
|
||||
},
|
||||
disconnect: function () {
|
||||
me.sender = noopSend;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
var debug = {}; //{flow: true, send: false, receive: false};
|
||||
function makeBaton(testKey, nodes, node, debug, mode, optimize) {
|
||||
debug = debug || {};
|
||||
var baton = virtualBaton({
|
||||
batonName: testKey,
|
||||
debugSend: debug.send,
|
||||
debugReceive: debug.receive,
|
||||
debugFlow: debug.flow,
|
||||
useOptimizations: optimize,
|
||||
connectionTest: function (id) {
|
||||
return baton.validId(id);
|
||||
},
|
||||
globals: {
|
||||
Messages: makeMessager(nodes, node, mode),
|
||||
MyAvatar: {sessionUUID: node.name},
|
||||
Script: {
|
||||
setTimeout: setTimeout,
|
||||
clearTimeout: clearTimeout,
|
||||
setInterval: setInterval,
|
||||
clearInterval: clearInterval
|
||||
},
|
||||
AvatarList: {
|
||||
getAvatar: function (id) {
|
||||
return {sessionUUID: id};
|
||||
}
|
||||
},
|
||||
Entities: {getEntityProperties: function () {
|
||||
}},
|
||||
print: console.log
|
||||
}
|
||||
});
|
||||
return baton;
|
||||
}
|
||||
function noRelease(batonName) {
|
||||
assert.ok(!batonName, "should not release");
|
||||
}
|
||||
function defineABunch(mode, optimize) {
|
||||
function makeKey(prefix) {
|
||||
return prefix + mode + (optimize ? '-opt' : '');
|
||||
}
|
||||
var testKeys = makeKey('single-');
|
||||
it(testKeys, function (done) {
|
||||
var nodes = [{name: 'a'}];
|
||||
var a = makeBaton(testKeys, nodes, nodes[0], debug, mode).claim(function (key) {
|
||||
console.log('claimed a');
|
||||
assert.equal(testKeys, key);
|
||||
a.unload();
|
||||
done();
|
||||
}, noRelease);
|
||||
});
|
||||
var testKeydp = makeKey('dual-parallel-');
|
||||
it(testKeydp, function (done) {
|
||||
this.timeout(10000);
|
||||
var nodes = [{name: 'ap'}, {name: 'bp'}];
|
||||
var a = makeBaton(testKeydp, nodes, nodes[0], debug, mode, optimize),
|
||||
b = makeBaton(testKeydp, nodes, nodes[1], debug, mode, optimize);
|
||||
function accepted(key) { // Under some circumstances of network timing, either a or b can win.
|
||||
console.log('claimed ap');
|
||||
assert.equal(testKeydp, key);
|
||||
done();
|
||||
}
|
||||
a.claim(accepted, noRelease);
|
||||
b.claim(accepted, noRelease);
|
||||
});
|
||||
var testKeyds = makeKey('dual-serial-');
|
||||
it(testKeyds, function (done) {
|
||||
var nodes = [{name: 'as'}, {name: 'bs'}],
|
||||
gotA = false,
|
||||
gotB = false;
|
||||
makeBaton(testKeyds, nodes, nodes[0], debug, mode, optimize).claim(function (key) {
|
||||
console.log('claimed as', key);
|
||||
assert.ok(!gotA, "should not get A after B");
|
||||
gotA = true;
|
||||
done();
|
||||
}, noRelease);
|
||||
setTimeout(function () {
|
||||
makeBaton(testKeyds, nodes, nodes[1], debug, mode, optimize).claim(function (key) {
|
||||
console.log('claimed bs', key);
|
||||
assert.ok(!gotB, "should not get B after A");
|
||||
gotB = true;
|
||||
done();
|
||||
}, noRelease);
|
||||
}, 500);
|
||||
});
|
||||
var testKeydsl = makeKey('dual-serial-long-');
|
||||
it(testKeydsl, function (done) {
|
||||
this.timeout(5000);
|
||||
var nodes = [{name: 'al'}, {name: 'bl'}],
|
||||
gotA = false,
|
||||
gotB = false,
|
||||
releaseA = false;
|
||||
makeBaton(testKeydsl, nodes, nodes[0], debug, mode, optimize).claim(function (key) {
|
||||
console.log('claimed al', key);
|
||||
assert.ok(!gotB, "should not get A after B");
|
||||
gotA = true;
|
||||
if (!gotB) {
|
||||
done();
|
||||
}
|
||||
}, function () {
|
||||
assert.ok(gotA, "Should claim it first");
|
||||
releaseA = true;
|
||||
if (gotB) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
setTimeout(function () {
|
||||
makeBaton(testKeydsl, nodes, nodes[1], debug, mode, optimize).claim(function (key) {
|
||||
console.log('claimed bl', key);
|
||||
gotB = true;
|
||||
if (releaseA) {
|
||||
done();
|
||||
}
|
||||
}, noRelease);
|
||||
}, 3000);
|
||||
});
|
||||
var testKeydsr = makeKey('dual-serial-with-release-');
|
||||
it(testKeydsr, function (done) {
|
||||
this.timeout(5000);
|
||||
var nodes = [{name: 'asr'}, {name: 'bsr'}],
|
||||
gotClaimA = false,
|
||||
gotReleaseA = false,
|
||||
a = makeBaton(testKeydsr, nodes, nodes[0], debug, mode, optimize),
|
||||
b = makeBaton(testKeydsr, nodes, nodes[1], debug, mode, optimize);
|
||||
a.claim(function (key) {
|
||||
console.log('claimed asr');
|
||||
assert.equal(testKeydsr, key);
|
||||
gotClaimA = true;
|
||||
b.claim(function (key) {
|
||||
console.log('claimed bsr');
|
||||
assert.equal(testKeydsr, key);
|
||||
assert.ok(gotReleaseA);
|
||||
done();
|
||||
}, noRelease);
|
||||
a.release();
|
||||
}, function (key) {
|
||||
console.log('released asr');
|
||||
assert.equal(testKeydsr, key);
|
||||
assert.ok(gotClaimA);
|
||||
gotReleaseA = true;
|
||||
});
|
||||
});
|
||||
var testKeydpr = makeKey('dual-parallel-with-release-');
|
||||
it(testKeydpr, function (done) {
|
||||
this.timeout(5000);
|
||||
var nodes = [{name: 'ar'}, {name: 'br'}];
|
||||
var a = makeBaton(testKeydpr, nodes, nodes[0], debug, mode, optimize),
|
||||
b = makeBaton(testKeydpr, nodes, nodes[1], debug, mode, optimize),
|
||||
gotClaimA = false,
|
||||
gotReleaseA = false,
|
||||
gotClaimB = false;
|
||||
a.claim(function (key) {
|
||||
console.log('claimed ar');
|
||||
assert.equal(testKeydpr, key);
|
||||
gotClaimA = true;
|
||||
assert.ok(!gotClaimB, "if b claimed, should not get a");
|
||||
a.release();
|
||||
}, function (key) {
|
||||
console.log('released ar');
|
||||
assert.equal(testKeydpr, key);
|
||||
assert.ok(gotClaimA);
|
||||
gotReleaseA = true;
|
||||
});
|
||||
b.claim(function (key) {
|
||||
console.log('claimed br', gotClaimA ? 'with' : 'without', 'ar first');
|
||||
assert.equal(testKeydpr, key);
|
||||
gotClaimB = true;
|
||||
assert.ok(!gotClaimA || gotReleaseA);
|
||||
done();
|
||||
}, noRelease);
|
||||
});
|
||||
}
|
||||
function defineAllModeTests(optimize) {
|
||||
defineABunch('delayed', optimize);
|
||||
defineABunch('immediate2Me', optimize);
|
||||
defineABunch('immediate', optimize);
|
||||
}
|
||||
defineAllModeTests(true);
|
||||
defineAllModeTests(false);
|
||||
after(function () {
|
||||
console.log(messageCount, 'messages sent over', (Date.now() - testStart), 'ms.');
|
||||
});
|
||||
});
|
|
@ -18,21 +18,46 @@
|
|||
var self = this;
|
||||
|
||||
this.preload = function(entityId) {
|
||||
|
||||
this.entityId = entityId;
|
||||
this.data = JSON.parse(Entities.getEntityProperties(this.entityId, "userData").userData);
|
||||
this.buttonImageURL = baseURL + "GUI/GUI_jump_off.png";
|
||||
this.addExitButton();
|
||||
this.isRiding = false;
|
||||
|
||||
if (this.data && this.data.isDynein) {
|
||||
this.rotation = 180;
|
||||
} else {
|
||||
this.rotation = 0;
|
||||
}
|
||||
|
||||
this.initialize(entityId);
|
||||
self.initTimeout = null;
|
||||
}
|
||||
|
||||
this.initialize = function(entityId) {
|
||||
//print(' should initialize' + entityId)
|
||||
var properties = Entities.getEntityProperties(entityId);
|
||||
if (properties.userData.length === 0 || properties.hasOwnProperty('userData') === false) {
|
||||
self.initTimeout = Script.setTimeout(function() {
|
||||
//print(' no user data yet, try again in one second')
|
||||
self.initialize(entityId);
|
||||
}, 1000)
|
||||
|
||||
} else {
|
||||
//print(' userdata before parse attempt' + properties.userData)
|
||||
self.userData = null;
|
||||
try {
|
||||
self.userData = JSON.parse(properties.userData);
|
||||
} catch (err) {
|
||||
//print(' error parsing json');
|
||||
//print(' properties are:' + properties.userData);
|
||||
return;
|
||||
}
|
||||
|
||||
self.data = self.userData;
|
||||
self.buttonImageURL = baseURL + "GUI/GUI_jump_off.png";
|
||||
self.addExitButton();
|
||||
self.isRiding = false;
|
||||
self.mouseIsConnected = false;
|
||||
if (self.data && self.data.isDynein) {
|
||||
self.rotation = 180;
|
||||
} else {
|
||||
self.rotation = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.addExitButton = function() {
|
||||
this.windowDimensions = Controller.getViewportDimensions();
|
||||
this.buttonWidth = 75;
|
||||
|
@ -53,19 +78,22 @@
|
|||
}
|
||||
|
||||
this.clickReleaseOnEntity = function(entityId, mouseEvent) {
|
||||
// print('CLICKED ON MOTOR PROTEIN')
|
||||
|
||||
//print('CLICKED ON MOTOR PROTEIN')
|
||||
return;
|
||||
if (mouseEvent.isLeftButton && !self.isRiding) {
|
||||
print("GET ON");
|
||||
//print("GET ON");
|
||||
self.isRiding = true;
|
||||
if (!self.entityId) {
|
||||
self.entityId = entityId;
|
||||
}
|
||||
self.entityLocation = Entities.getEntityProperties(this.entityId, "position").position;
|
||||
self.entityLocation = Entities.getEntityProperties(self.entityId, "position").position;
|
||||
self.targetLocation = Vec3.sum(self.entityLocation, TARGET_OFFSET);
|
||||
Overlays.editOverlay(self.exitButton, {
|
||||
visible: true
|
||||
});
|
||||
Controller.mousePressEvent.connect(this.onMousePress);
|
||||
self.mouseIsConnected = true;
|
||||
Script.update.connect(this.update);
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +144,7 @@
|
|||
y: event.y
|
||||
});
|
||||
if (event.isLeftButton && clickedOverlay === self.exitButton) {
|
||||
print("GET OFF");
|
||||
//print("GET OFF");
|
||||
Script.update.disconnect(this.update);
|
||||
self.reset();
|
||||
}
|
||||
|
@ -136,8 +164,12 @@
|
|||
// print("unload");
|
||||
self.reset();
|
||||
|
||||
Controller.mousePressEvent.disconnect(this.onMousePress);
|
||||
if (self.mouseIsConnected === true) {
|
||||
Controller.mousePressEvent.disconnect(self.onMousePress);
|
||||
}
|
||||
if (self.initTimeout !== null) {
|
||||
Script.clearTimeout(self.initTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function deleteAllInRadius(r) {
|
||||
var n = 0;
|
||||
var arrayFound = Entities.findEntities(MyAvatar.position, r);
|
||||
for (var i = 0; i < arrayFound.length; i++) {
|
||||
Entities.deleteEntity(arrayFound[i]);
|
||||
}
|
||||
print("deleted " + arrayFound.length + " entities");
|
||||
}
|
||||
|
||||
deleteAllInRadius(100000);
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var scriptName = "Controller";
|
||||
|
||||
function findScriptsInRadius(r) {
|
||||
var n = 0;
|
||||
var arrayFound = Entities.findEntities(MyAvatar.position, r);
|
||||
for (var i = 0; i < arrayFound.length; i++) {
|
||||
if (Entities.getEntityProperties(arrayFound[i]).script.indexOf(scriptName) != -1) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
print("found " + n + " copies of " + scriptName);
|
||||
}
|
||||
|
||||
findScriptsInRadius(100000);
|
|
@ -9,7 +9,8 @@
|
|||
var self = this;
|
||||
|
||||
this.preload = function(entityId) {
|
||||
|
||||
//print('preload move randomly')
|
||||
this.isConnected = false;
|
||||
this.entityId = entityId;
|
||||
this.updateInterval = 100;
|
||||
this.posFrame = 0;
|
||||
|
@ -21,62 +22,161 @@
|
|||
this.minAngularVelocity = 0.01;
|
||||
this.maxAngularVelocity = 0.03;
|
||||
|
||||
this.initialize(entityId);
|
||||
this.initTimeout = null;
|
||||
|
||||
|
||||
var userData = {
|
||||
ownershipKey: {
|
||||
owner: MyAvatar.sessionUUID
|
||||
},
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
};
|
||||
|
||||
Entities.editEntity(entityId, {
|
||||
userData: JSON.stringify(userData)
|
||||
})
|
||||
}
|
||||
|
||||
this.initialize = function(entityId) {
|
||||
//print('move randomly should initialize' + entityId)
|
||||
var properties = Entities.getEntityProperties(entityId);
|
||||
if (properties.userData.length === 0 || properties.hasOwnProperty('userData') === false) {
|
||||
self.initTimeout = Script.setTimeout(function() {
|
||||
//print('no user data yet, try again in one second')
|
||||
self.initialize(entityId);
|
||||
}, 1000)
|
||||
|
||||
} else {
|
||||
//print('userdata before parse attempt' + properties.userData)
|
||||
self.userData = null;
|
||||
try {
|
||||
self.userData = JSON.parse(properties.userData);
|
||||
} catch (err) {
|
||||
//print('error parsing json');
|
||||
//print('properties are:' + properties.userData);
|
||||
return;
|
||||
}
|
||||
Script.update.connect(self.update);
|
||||
this.isConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.update = function(deltaTime) {
|
||||
// print('jbp in update')
|
||||
var data = Entities.getEntityProperties(self.entityId, 'userData').userData;
|
||||
var userData;
|
||||
try {
|
||||
userData = JSON.parse(data)
|
||||
} catch (e) {
|
||||
//print('error parsing json' + data)
|
||||
return;
|
||||
};
|
||||
|
||||
self.posFrame++;
|
||||
self.rotFrame++;
|
||||
|
||||
if (self.posFrame > self.posInterval) {
|
||||
|
||||
self.posInterval = 100 * Math.random() + 300;
|
||||
self.posFrame = 0;
|
||||
|
||||
var magnitudeV = self.maxVelocity;
|
||||
var directionV = {
|
||||
x: Math.random() - 0.5,
|
||||
y: Math.random() - 0.5,
|
||||
z: Math.random() - 0.5
|
||||
};
|
||||
|
||||
// print("POS magnitude is " + magnitudeV + " and direction is " + directionV.x);
|
||||
Entities.editEntity(self.entityId, {
|
||||
velocity: Vec3.multiply(magnitudeV, Vec3.normalize(directionV))
|
||||
|
||||
});
|
||||
|
||||
// print('userdata is' + data)
|
||||
//if the entity doesnt have an owner set yet
|
||||
if (userData.hasOwnProperty('ownershipKey') !== true) {
|
||||
//print('no movement owner yet')
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.rotFrame > self.rotInterval) {
|
||||
//print('owner is:::' + userData.ownershipKey.owner)
|
||||
//get all the avatars to see if the owner is around
|
||||
var avatars = AvatarList.getAvatarIdentifiers();
|
||||
var ownerIsAround = false;
|
||||
|
||||
self.rotInterval = 100 * Math.random() + 250;
|
||||
self.rotFrame = 0;
|
||||
//if the current owner is not me...
|
||||
if (userData.ownershipKey.owner !== MyAvatar.sessionUUID) {
|
||||
|
||||
var magnitudeAV = self.maxAngularVelocity;
|
||||
//look to see if the current owner is around anymore
|
||||
for (var i = 0; i < avatars.length; i++) {
|
||||
if (avatars[i] === userData.ownershipKey.owner) {
|
||||
ownerIsAround = true
|
||||
//the owner is around
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
var directionAV = {
|
||||
x: Math.random() - 0.5,
|
||||
y: Math.random() - 0.5,
|
||||
z: Math.random() - 0.5
|
||||
};
|
||||
// print("ROT magnitude is " + magnitudeAV + " and direction is " + directionAV.x);
|
||||
Entities.editEntity(self.entityId, {
|
||||
angularVelocity: Vec3.multiply(magnitudeAV, Vec3.normalize(directionAV))
|
||||
//if the owner is not around, then take ownership
|
||||
if (ownerIsAround === false) {
|
||||
//print('taking ownership')
|
||||
|
||||
});
|
||||
var userData = {
|
||||
ownershipKey: {
|
||||
owner: MyAvatar.sessionUUID
|
||||
},
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
};
|
||||
Entities.editEntity(self.entityId, {
|
||||
userData: JSON.stringify(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
//but if the current owner IS me, then move it
|
||||
else {
|
||||
//print('jbp im the owner so move it')
|
||||
self.posFrame++;
|
||||
self.rotFrame++;
|
||||
|
||||
if (self.posFrame > self.posInterval) {
|
||||
|
||||
self.posInterval = 100 * Math.random() + 300;
|
||||
self.posFrame = 0;
|
||||
|
||||
var magnitudeV = self.maxVelocity;
|
||||
var directionV = {
|
||||
x: Math.random() - 0.5,
|
||||
y: Math.random() - 0.5,
|
||||
z: Math.random() - 0.5
|
||||
};
|
||||
|
||||
//print("POS magnitude is " + magnitudeV + " and direction is " + directionV.x);
|
||||
Entities.editEntity(self.entityId, {
|
||||
velocity: Vec3.multiply(magnitudeV, Vec3.normalize(directionV))
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (self.rotFrame > self.rotInterval) {
|
||||
|
||||
self.rotInterval = 100 * Math.random() + 250;
|
||||
self.rotFrame = 0;
|
||||
|
||||
var magnitudeAV = self.maxAngularVelocity;
|
||||
|
||||
var directionAV = {
|
||||
x: Math.random() - 0.5,
|
||||
y: Math.random() - 0.5,
|
||||
z: Math.random() - 0.5
|
||||
};
|
||||
//print("ROT magnitude is " + magnitudeAV + " and direction is " + directionAV.x);
|
||||
Entities.editEntity(self.entityId, {
|
||||
angularVelocity: Vec3.multiply(magnitudeAV, Vec3.normalize(directionAV))
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
this.unload = function() {
|
||||
if (this.initTimeout !== null) {
|
||||
Script.clearTimeout(this.initTimeout);
|
||||
}
|
||||
|
||||
Script.update.disconnect(this.update);
|
||||
if (this.isConnected === true) {
|
||||
Script.update.disconnect(this.update);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Script.update.connect(this.update);
|
||||
|
||||
|
||||
})
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
|
@ -8,7 +7,7 @@
|
|||
|
||||
(function() {
|
||||
|
||||
var version = 1;
|
||||
var version = 11;
|
||||
var added = false;
|
||||
this.frame = 0;
|
||||
var utilsScript = Script.resolvePath('utils.js');
|
||||
|
@ -19,35 +18,61 @@
|
|||
|
||||
this.preload = function(entityId) {
|
||||
this.entityId = entityId;
|
||||
var mySavedSettings = Settings.getValue(entityId);
|
||||
this.initialize(entityId);
|
||||
this.initTimeout = null;
|
||||
}
|
||||
|
||||
if (mySavedSettings.buttons !== undefined) {
|
||||
// print('NAV preload buttons'+ mySavedSettings.buttons)
|
||||
mySavedSettings.buttons.forEach(function(b) {
|
||||
// print('NAV deleting button'+ b)
|
||||
Overlays.deleteOverlay(b);
|
||||
})
|
||||
Settings.setValue(entityId,'')
|
||||
}
|
||||
|
||||
|
||||
self.getUserData();
|
||||
this.buttonImageURL = baseURL + "GUI/GUI_" + self.userData.name + ".png?" + version;
|
||||
if (self.button === undefined) {
|
||||
// print('NAV NO BUTTON ADDING ONE!!')
|
||||
self.button = true;
|
||||
self.addButton();
|
||||
this.initialize = function(entityId) {
|
||||
print('JBP nav button should initialize' + entityId)
|
||||
var properties = Entities.getEntityProperties(entityId);
|
||||
if (properties.userData.length === 0 || properties.hasOwnProperty('userData') === false) {
|
||||
self.initTimeout = Script.setTimeout(function() {
|
||||
print('JBP no user data yet, try again in one second')
|
||||
self.initialize(entityId);
|
||||
}, 1000)
|
||||
|
||||
} else {
|
||||
// print('NAV SELF ALREADY HAS A BUTTON!!')
|
||||
}
|
||||
print('JBP userdata before parse attempt' + properties.userData)
|
||||
self.userData = null;
|
||||
try {
|
||||
self.userData = JSON.parse(properties.userData);
|
||||
} catch (err) {
|
||||
print('JBP error parsing json');
|
||||
print('JBP properties are:' + properties.userData);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var mySavedSettings = Settings.getValue(entityId);
|
||||
|
||||
if (mySavedSettings.buttons !== undefined) {
|
||||
print('JBP preload buttons' + mySavedSettings.buttons)
|
||||
mySavedSettings.buttons.forEach(function(b) {
|
||||
print('JBP deleting button' + b)
|
||||
Overlays.deleteOverlay(b);
|
||||
})
|
||||
Settings.setValue(entityId, '')
|
||||
}
|
||||
|
||||
|
||||
self.buttonImageURL = baseURL + "GUI/GUI_" + self.userData.name + ".png?" + version;
|
||||
print('JBP BUTTON IMAGE URL:' + self.buttonImageURL)
|
||||
if (self.button === undefined) {
|
||||
// print('NAV NO BUTTON ADDING ONE!!')
|
||||
self.button = true;
|
||||
self.addButton();
|
||||
|
||||
} else {
|
||||
// print('NAV SELF ALREADY HAS A BUTTON!!')
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.addButton = function() {
|
||||
|
||||
|
||||
self.getUserData();
|
||||
this.windowDimensions = Controller.getViewportDimensions();
|
||||
this.buttonWidth = 150;
|
||||
this.buttonHeight = 50;
|
||||
|
@ -87,7 +112,7 @@
|
|||
if (self.frame < 10) {
|
||||
self.frame++;
|
||||
} else {
|
||||
// this.lookAt(this.userData.target);
|
||||
// this.lookAt(this.userData.target);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +132,7 @@
|
|||
}
|
||||
|
||||
this.lookAtTarget = function() {
|
||||
self.getUserData();
|
||||
|
||||
var direction = Vec3.normalize(Vec3.subtract(self.userData.entryPoint, self.userData.target));
|
||||
var pitch = Quat.angleAxis(Math.asin(-direction.y) * 180.0 / Math.PI, {
|
||||
x: 1,
|
||||
|
@ -125,16 +150,6 @@
|
|||
MyAvatar.headYaw = 0;
|
||||
|
||||
}
|
||||
|
||||
this.getUserData = function() {
|
||||
this.properties = Entities.getEntityProperties(this.entityId);
|
||||
if (self.properties.userData) {
|
||||
this.userData = JSON.parse(this.properties.userData);
|
||||
} else {
|
||||
this.userData = {};
|
||||
}
|
||||
}
|
||||
|
||||
var buttonDeleter;
|
||||
var deleterCount = 0;
|
||||
this.unload = function() {
|
||||
|
@ -144,6 +159,11 @@
|
|||
|
||||
Controller.mousePressEvent.disconnect(this.onClick);
|
||||
// Script.update.disconnect(this.update);
|
||||
|
||||
|
||||
if (this.initTimeout !== null) {
|
||||
Script.clearTimeout(this.initTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(this.onClick);
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() {
|
||||
var self = this;
|
||||
var baseURL = "https://hifi-content.s3.amazonaws.com/DomainContent/CellScience/";
|
||||
var version = 9;
|
||||
this.preload = function(entityId) {
|
||||
self.soundPlaying = false;
|
||||
self.entityId = entityId;
|
||||
self.getUserData();
|
||||
self.soundURL = baseURL + "Audio/" + self.userData.name + ".wav?" + version;
|
||||
print("Script.clearTimeout creating WAV name location is " + baseURL + "Audio/" + self.userData.name + ".wav");
|
||||
|
||||
self.soundOptions = {
|
||||
stereo: true,
|
||||
loop: true,
|
||||
localOnly: true,
|
||||
volume: 0.035
|
||||
};
|
||||
|
||||
this.sound = SoundCache.getSound(self.soundURL);
|
||||
|
||||
}
|
||||
|
||||
this.getUserData = function() {
|
||||
self.properties = Entities.getEntityProperties(self.entityId);
|
||||
if (self.properties.userData) {
|
||||
self.userData = JSON.parse(this.properties.userData);
|
||||
} else {
|
||||
self.userData = {};
|
||||
}
|
||||
}
|
||||
|
||||
this.enterEntity = function(entityID) {
|
||||
print("entering audio zone");
|
||||
if (self.sound.downloaded) {
|
||||
print("playing background audio named " + self.userData.name + "which has been downloaded");
|
||||
this.soundPlaying = Audio.playSound(self.sound, self.soundOptions);
|
||||
|
||||
} else {
|
||||
print("sound is not downloaded");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.leaveEntity = function(entityID) {
|
||||
print("leaving audio area " + self.userData.name);
|
||||
if (self.soundPlaying !== false) {
|
||||
print("not null");
|
||||
print("Stopped sound " + self.userData.name);
|
||||
self.soundPlaying.stop();
|
||||
} else {
|
||||
print("Sound not playing");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
});
|
|
@ -6,97 +6,117 @@
|
|||
//
|
||||
|
||||
(function() {
|
||||
var baseURL = "https://hifi-content.s3.amazonaws.com/hifi-content/DomainContent/CellScience/";
|
||||
var self = this;
|
||||
this.buttonImageURL = baseURL + "GUI/play_audio.svg?2";
|
||||
var baseURL = "https://hifi-content.s3.amazonaws.com/hifi-content/DomainContent/CellScience/";
|
||||
var self = this;
|
||||
this.buttonImageURL = baseURL + "GUI/play_audio.svg?2";
|
||||
|
||||
|
||||
|
||||
this.preload = function(entityId) {
|
||||
this.entityId = entityId;
|
||||
self.addButton();
|
||||
this.buttonShowing = false;
|
||||
self.getUserData();
|
||||
this.showDistance = self.userData.showDistance;
|
||||
this.soundURL = baseURL + "Audio/" + self.userData.soundName + ".wav";
|
||||
print("distance = " + self.userData.showDistance + ", sound = " + this.soundURL);
|
||||
this.soundOptions = {
|
||||
stereo: true,
|
||||
loop: false,
|
||||
localOnly: true,
|
||||
volume: 0.035
|
||||
};
|
||||
this.sound = SoundCache.getSound(this.soundURL);
|
||||
}
|
||||
|
||||
this.addButton = function() {
|
||||
this.windowDimensions = Controller.getViewportDimensions();
|
||||
this.buttonWidth = 100;
|
||||
this.buttonHeight = 100;
|
||||
this.buttonPadding = 0;
|
||||
|
||||
this.buttonPositionX = (self.windowDimensions.x - self.buttonPadding) / 2 - self.buttonWidth;
|
||||
this.buttonPositionY = (self.windowDimensions.y - self.buttonHeight) - (self.buttonHeight + self.buttonPadding);
|
||||
this.button = Overlays.addOverlay("image", {
|
||||
x: self.buttonPositionX,
|
||||
y: self.buttonPositionY,
|
||||
width: self.buttonWidth,
|
||||
height: self.buttonHeight,
|
||||
imageURL: self.buttonImageURL,
|
||||
visible: false,
|
||||
alpha: 1.0
|
||||
});
|
||||
}
|
||||
|
||||
this.getUserData = function() {
|
||||
this.properties = Entities.getEntityProperties(this.entityId);
|
||||
if (self.properties.userData) {
|
||||
this.userData = JSON.parse(this.properties.userData);
|
||||
} else {
|
||||
this.userData = {};
|
||||
this.preload = function(entityId) {
|
||||
this.entityId = entityId;
|
||||
this.initialize(entityId)
|
||||
this.initTimeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.update = function(deltaTime) {
|
||||
this.initialize = function(entityId) {
|
||||
//print(' should initialize' + entityId)
|
||||
var properties = Entities.getEntityProperties(entityId);
|
||||
if (properties.userData.length === 0 || properties.hasOwnProperty('userData') === false) {
|
||||
self.initTimeout = Script.setTimeout(function() {
|
||||
// print(' no user data yet, try again in one second')
|
||||
self.initialize(entityId);
|
||||
}, 1000)
|
||||
|
||||
self.distance = Vec3.distance(MyAvatar.position, Entities.getEntityProperties(self.entityId).position);
|
||||
//print(self.distance);
|
||||
if (!self.buttonShowing && self.distance < self.userData.showDistance) {
|
||||
self.buttonShowing = true;
|
||||
Overlays.editOverlay(self.button, {
|
||||
visible: true
|
||||
});
|
||||
} else if (self.buttonShowing && self.distance > self.userData.showDistance) {
|
||||
self.buttonShowing = false;
|
||||
Overlays.editOverlay(self.button, {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.onClick = function(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||
x: event.x,
|
||||
y: event.y
|
||||
});
|
||||
if (clickedOverlay === self.button) {
|
||||
print("button was clicked");
|
||||
if (self.sound.downloaded) {
|
||||
print("play sound");
|
||||
Audio.playSound(self.sound, self.soundOptions);
|
||||
} else {
|
||||
print("not downloaded");
|
||||
//print(' userdata before parse attempt' + properties.userData)
|
||||
self.userData = null;
|
||||
try {
|
||||
self.userData = JSON.parse(properties.userData);
|
||||
} catch (err) {
|
||||
// print(' error parsing json');
|
||||
// print(' properties are:' + properties.userData);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
self.addButton();
|
||||
self.buttonShowing = false;
|
||||
self.showDistance = self.userData.showDistance;
|
||||
self.soundURL = baseURL + "Audio/" + self.userData.soundName + ".wav";
|
||||
// print("distance = " + self.userData.showDistance + ", sound = " + self.soundURL);
|
||||
self.soundOptions = {
|
||||
stereo: true,
|
||||
loop: false,
|
||||
localOnly: true,
|
||||
volume: 1
|
||||
};
|
||||
self.sound = SoundCache.getSound(this.soundURL);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.unload = function() {
|
||||
Overlays.deleteOverlay(self.button);
|
||||
Controller.mousePressEvent.disconnect(this.onClick);
|
||||
Script.update.disconnect(this.update);
|
||||
}
|
||||
this.addButton = function() {
|
||||
this.windowDimensions = Controller.getViewportDimensions();
|
||||
this.buttonWidth = 100;
|
||||
this.buttonHeight = 100;
|
||||
this.buttonPadding = 0;
|
||||
|
||||
Controller.mousePressEvent.connect(this.onClick);
|
||||
Script.update.connect(this.update);
|
||||
this.buttonPositionX = (self.windowDimensions.x - self.buttonPadding) / 2 - self.buttonWidth;
|
||||
this.buttonPositionY = (self.windowDimensions.y - self.buttonHeight) - (self.buttonHeight + self.buttonPadding);
|
||||
this.button = Overlays.addOverlay("image", {
|
||||
x: self.buttonPositionX,
|
||||
y: self.buttonPositionY,
|
||||
width: self.buttonWidth,
|
||||
height: self.buttonHeight,
|
||||
imageURL: self.buttonImageURL,
|
||||
visible: false,
|
||||
alpha: 1.0
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
this.update = function(deltaTime) {
|
||||
|
||||
self.distance = Vec3.distance(MyAvatar.position, Entities.getEntityProperties(self.entityId).position);
|
||||
//print(self.distance);
|
||||
if (!self.buttonShowing && self.distance < self.userData.showDistance) {
|
||||
self.buttonShowing = true;
|
||||
Overlays.editOverlay(self.button, {
|
||||
visible: true
|
||||
});
|
||||
} else if (self.buttonShowing && self.distance > self.userData.showDistance) {
|
||||
self.buttonShowing = false;
|
||||
Overlays.editOverlay(self.button, {
|
||||
visible: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.onClick = function(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||
x: event.x,
|
||||
y: event.y
|
||||
});
|
||||
if (clickedOverlay === self.button) {
|
||||
//print("button was clicked");
|
||||
if (self.sound.downloaded) {
|
||||
//print("play sound");
|
||||
Audio.playSound(self.sound, self.soundOptions);
|
||||
} else {
|
||||
//print("not downloaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.unload = function() {
|
||||
Overlays.deleteOverlay(self.button);
|
||||
Controller.mousePressEvent.disconnect(this.onClick);
|
||||
Script.update.disconnect(this.update);
|
||||
if (this.initTimeout !== null) {
|
||||
Script.clearTimeout(this.initTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(this.onClick);
|
||||
Script.update.connect(this.update);
|
||||
|
||||
});
|
|
@ -10,24 +10,51 @@
|
|||
var self = this;
|
||||
var baseURL = "https://hifi-content.s3.amazonaws.com/DomainContent/CellScience/";
|
||||
|
||||
var version = 1;
|
||||
var version = 2;
|
||||
this.preload = function(entityId) {
|
||||
this.soundPlaying = null;
|
||||
this.entityId = entityId;
|
||||
self.getUserData();
|
||||
this.labelURL = baseURL + "GUI/labels_" + self.userData.name + ".png?" + version;
|
||||
this.showDistance = self.userData.showDistance;
|
||||
this.soundURL = baseURL + "Audio/" + self.userData.name + ".wav";
|
||||
this.soundOptions = {
|
||||
stereo: true,
|
||||
loop: false,
|
||||
localOnly: true,
|
||||
volume: 0.035,
|
||||
position: this.position
|
||||
};
|
||||
this.sound = SoundCache.getSound(this.soundURL);
|
||||
this.buttonImageURL = baseURL + "GUI/GUI_audio.png?" + version;
|
||||
self.addButton();
|
||||
self.initTimeout = null;
|
||||
this.initialize(entityId);
|
||||
|
||||
|
||||
}
|
||||
|
||||
this.initialize = function(entityId) {
|
||||
//print(' should initialize' + entityId)
|
||||
var properties = Entities.getEntityProperties(entityId);
|
||||
if (properties.userData.length === 0 || properties.hasOwnProperty('userData') === false) {
|
||||
self.initTimeout = Script.setTimeout(function() {
|
||||
//print(' no user data yet, try again in one second')
|
||||
self.initialize(entityId);
|
||||
}, 1000)
|
||||
|
||||
} else {
|
||||
//print(' userdata before parse attempt' + properties.userData)
|
||||
self.userData = null;
|
||||
try {
|
||||
self.userData = JSON.parse(properties.userData);
|
||||
} catch (err) {
|
||||
//print(' error parsing json');
|
||||
//print(' properties are:' + properties.userData);
|
||||
return;
|
||||
}
|
||||
|
||||
self.labelURL = baseURL + "GUI/labels_" + self.userData.name + ".png?" + version;
|
||||
self.showDistance = self.userData.showDistance;
|
||||
self.soundURL = baseURL + "Audio/" + self.userData.name + ".wav";
|
||||
self.soundOptions = {
|
||||
stereo: true,
|
||||
loop: false,
|
||||
localOnly: true,
|
||||
volume: 0.035,
|
||||
position: properties.position
|
||||
};
|
||||
self.sound = SoundCache.getSound(self.soundURL);
|
||||
self.buttonImageURL = baseURL + "GUI/GUI_audio.png?" + version;
|
||||
self.addButton();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.addButton = function() {
|
||||
|
@ -78,9 +105,8 @@
|
|||
|
||||
this.enterEntity = function(entityID) {
|
||||
|
||||
// self.getUserData();
|
||||
print("entering entity and showing" + self.labelURL);
|
||||
//self.buttonShowing = true;
|
||||
// print("entering entity and showing" + self.labelURL);
|
||||
|
||||
Overlays.editOverlay(self.button, {
|
||||
visible: true
|
||||
});
|
||||
|
@ -92,9 +118,8 @@
|
|||
|
||||
|
||||
this.leaveEntity = function(entityID) {
|
||||
// self.getUserData();
|
||||
// print("leaving entity " + self.userData.name);
|
||||
//self.buttonShowing = false;
|
||||
// print("leaving entity " + self.userData.name);
|
||||
|
||||
print(Overlays);
|
||||
Overlays.editOverlay(self.button, {
|
||||
visible: false
|
||||
|
@ -110,16 +135,16 @@
|
|||
y: event.y
|
||||
});
|
||||
if (clickedOverlay == self.button) {
|
||||
print("button was clicked");
|
||||
//print("button was clicked");
|
||||
if (self.sound.downloaded) {
|
||||
print("play sound");
|
||||
// print("play sound");
|
||||
|
||||
Overlays.editOverlay(self.button, {
|
||||
visible: false
|
||||
});
|
||||
this.soundPlaying = Audio.playSound(self.sound, self.soundOptions);
|
||||
} else {
|
||||
print("not downloaded");
|
||||
// print("not downloaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +154,9 @@
|
|||
if (this.soundPlaying !== null) {
|
||||
this.soundPlaying.stop();
|
||||
}
|
||||
|
||||
if (self.initTimeout !== null) {
|
||||
Script.clearTimeout(self.initTimeout);
|
||||
}
|
||||
Controller.mousePressEvent.disconnect(this.onClick);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ getEntityUserData = function(id) {
|
|||
try {
|
||||
results = JSON.parse(properties.userData);
|
||||
} catch (err) {
|
||||
logDebug(err);
|
||||
logDebug(properties.userData);
|
||||
// print('error parsing json');
|
||||
// print('properties are:'+ properties.userData);
|
||||
}
|
||||
}
|
||||
return results ? results : {};
|
||||
|
|
|
@ -14,50 +14,58 @@
|
|||
this.entered = true;
|
||||
|
||||
this.preload = function(entityID) {
|
||||
|
||||
this.entityId = entityID;
|
||||
this.initialize(entityID);
|
||||
this.initTimeout = null;
|
||||
}
|
||||
|
||||
this.initialize = function(entityID) {
|
||||
// print(' should initialize')
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
portalDestination = properties.userData;
|
||||
animationURL = properties.modelURL;
|
||||
this.soundOptions = {
|
||||
stereo: true,
|
||||
loop: false,
|
||||
localOnly: false,
|
||||
position: this.position,
|
||||
volume: 0.035
|
||||
};
|
||||
|
||||
this.teleportSound = SoundCache.getSound("https://hifi-content.s3.amazonaws.com/DomainContent/CellScience/Audio/whoosh.wav");
|
||||
//print('Script.clearTimeout PRELOADING A ZOOM ENTITY')
|
||||
print(" portal destination is " + portalDestination);
|
||||
if (properties.userData.length === 0 || properties.hasOwnProperty('userData') === false) {
|
||||
self.initTimeout = Script.setTimeout(function() {
|
||||
// print(' no user data yet, try again in one second')
|
||||
self.initialize(entityID);
|
||||
}, 1000)
|
||||
} else {
|
||||
// print(' has userData')
|
||||
self.portalDestination = properties.userData;
|
||||
animationURL = properties.modelURL;
|
||||
self.soundOptions = {
|
||||
stereo: true,
|
||||
loop: false,
|
||||
localOnly: false,
|
||||
position: properties.position,
|
||||
volume: 0.5
|
||||
};
|
||||
|
||||
self.teleportSound = SoundCache.getSound("https://hifi-content.s3.amazonaws.com/DomainContent/CellScience/Audio/whoosh.wav");
|
||||
// print(" portal destination is " + self.portalDestination);
|
||||
}
|
||||
}
|
||||
|
||||
this.enterEntity = function(entityID) {
|
||||
print('Script.clearTimeout ENTERED A BOUNDARY ENTITY, SHOULD ZOOM', entityID)
|
||||
|
||||
//print('ENTERED A BOUNDARY ENTITY, SHOULD ZOOM', entityID)
|
||||
var data = JSON.parse(Entities.getEntityProperties(this.entityId).userData);
|
||||
|
||||
|
||||
//print('DATA IS::' + data)
|
||||
if (data != null) {
|
||||
print("Teleporting to (" + data.location.x + ", " + data.location.y + ", " + data.location.z + ")");
|
||||
if (self.teleportSound.downloaded) {
|
||||
//print("play sound");
|
||||
Audio.playSound(self.teleportSound, self.soundOptions);
|
||||
} else {
|
||||
//print("not downloaded");
|
||||
}
|
||||
|
||||
this.lookAt(data.target, data.location);
|
||||
|
||||
MyAvatar.position = data.location;
|
||||
|
||||
// if (data.hasOwnProperty('entryPoint') && data.hasOwnProperty('target')) {
|
||||
// this.lookAtTarget(data.entryPoint, data.target);
|
||||
// }
|
||||
// else{
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.lookAt = function(targetPosition, avatarPosition) {
|
||||
var direction = Vec3.normalize(Vec3.subtract(MyAvatar.position, targetPosition));
|
||||
|
||||
this.lookAtTarget = function(entryPoint,target) {
|
||||
//print('SHOULD LOOK AT TARGET')
|
||||
var direction = Vec3.normalize(Vec3.subtract(entryPoint, target));
|
||||
var pitch = Quat.angleAxis(Math.asin(-direction.y) * 180.0 / Math.PI, {
|
||||
x: 1,
|
||||
y: 0,
|
||||
|
@ -69,8 +77,10 @@
|
|||
z: 0
|
||||
});
|
||||
|
||||
MyAvatar.goToLocation(avatarPosition, true, yaw);
|
||||
MyAvatar.goToLocation(entryPoint, true, yaw);
|
||||
|
||||
MyAvatar.headYaw = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,9 +91,18 @@
|
|||
animationSettings: '{ "frameIndex": 1, "running": false }'
|
||||
});
|
||||
this.entered = false;
|
||||
if (this.initTimeout !== null) {
|
||||
Script.clearTimeout(this.initTimeout);
|
||||
}
|
||||
//playSound();
|
||||
}
|
||||
|
||||
this.unload = function() {
|
||||
if (this.initTimeout !== null) {
|
||||
Script.clearTimeout(this.initTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
this.hoverEnterEntity = function(entityID) {
|
||||
Entities.editEntity(entityID, {
|
||||
animationURL: animationURL,
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
var soundMap = [{
|
||||
name: 'Cells',
|
||||
url: "http://hifi-content.s3.amazonaws.com/DomainContent/CellScience/Audio/Cells.wav",
|
||||
audioOptions: {
|
||||
position: {
|
||||
x: 15850,
|
||||
y: 15850,
|
||||
z: 15850
|
||||
},
|
||||
volume: 0.1,
|
||||
loop: true
|
||||
}
|
||||
}, {
|
||||
name: 'Cell Layout',
|
||||
url: "http://hifi-content.s3.amazonaws.com/DomainContent/CellScience/Audio/CellLayout.wav",
|
||||
audioOptions: {
|
||||
position: {
|
||||
x: 15950,
|
||||
y: 15950,
|
||||
z: 15950
|
||||
},
|
||||
volume: 0.1,
|
||||
loop: true
|
||||
}
|
||||
}, {
|
||||
name: 'Ribsome',
|
||||
url: "http://hifi-content.s3.amazonaws.com/DomainContent/CellScience/Audio/Ribosome.wav",
|
||||
audioOptions: {
|
||||
position: {
|
||||
x: 15650,
|
||||
y: 15650,
|
||||
z: 15650
|
||||
},
|
||||
volume: 0.1,
|
||||
loop: true
|
||||
}
|
||||
}, {
|
||||
name: 'Hexokinase',
|
||||
url: "http://hifi-content.s3.amazonaws.com/DomainContent/CellScience/Audio/Hexokinase.wav",
|
||||
audioOptions: {
|
||||
position: {
|
||||
x: 15750,
|
||||
y: 15750,
|
||||
z: 15750
|
||||
},
|
||||
volume: 0.1,
|
||||
loop: true
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
|
||||
function loadSounds() {
|
||||
soundMap.forEach(function(soundData) {
|
||||
soundData.sound = SoundCache.getSound(soundData.url);
|
||||
});
|
||||
}
|
||||
|
||||
function playSound(soundData) {
|
||||
if (soundData.injector) {
|
||||
// try/catch in case the injector QObject has been deleted already
|
||||
try {
|
||||
soundData.injector.stop();
|
||||
} catch (e) {
|
||||
print('error playing sound' + e)
|
||||
}
|
||||
}
|
||||
soundData.injector = Audio.playSound(soundData.sound, soundData.audioOptions);
|
||||
}
|
||||
|
||||
function checkDownloaded(soundData) {
|
||||
if (soundData.sound.downloaded) {
|
||||
|
||||
Script.clearInterval(soundData.downloadTimer);
|
||||
|
||||
if (soundData.hasOwnProperty('playAtInterval')) {
|
||||
soundData.playingInterval = Script.setInterval(function() {
|
||||
playSound(soundData)
|
||||
}, soundData.playAtInterval);
|
||||
} else {
|
||||
playSound(soundData);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function startCheckDownloadedTimers() {
|
||||
soundMap.forEach(function(soundData) {
|
||||
soundData.downloadTimer = Script.setInterval(function() {
|
||||
checkDownloaded(soundData);
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
soundMap.forEach(function(soundData) {
|
||||
|
||||
if (soundData.hasOwnProperty("injector")) {
|
||||
soundData.injector.stop();
|
||||
}
|
||||
|
||||
if (soundData.hasOwnProperty("downloadTimer")) {
|
||||
Script.clearInterval(soundData.downloadTimer);
|
||||
}
|
||||
|
||||
if (soundData.hasOwnProperty("playingInterval")) {
|
||||
Script.clearInterval(soundData.playingInterval);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
loadSounds();
|
||||
startCheckDownloadedTimers();
|
|
@ -5,7 +5,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var version = 1004;
|
||||
var version = 1035;
|
||||
var cellLayout;
|
||||
var baseLocation = "https://hifi-content.s3.amazonaws.com/DomainContent/CellScience/";
|
||||
|
||||
|
@ -79,398 +79,138 @@ var locations = {
|
|||
}, 1000]
|
||||
};
|
||||
|
||||
var scenes = [
|
||||
{
|
||||
name: "Cells",
|
||||
objects: "",
|
||||
location: locations.cells[0],
|
||||
entryPoint: locations.cells[1],
|
||||
zone: {
|
||||
dimensions: {
|
||||
x: 4000,
|
||||
y: 4000,
|
||||
z: 4000
|
||||
},
|
||||
light: {
|
||||
r: 255,
|
||||
g: 200,
|
||||
b: 200
|
||||
},
|
||||
intensity: 1.1,
|
||||
ambient: 0.7,
|
||||
sun: true,
|
||||
skybox: "cells_skybox_cross"
|
||||
var scenes = [{
|
||||
name: "Cells",
|
||||
objects: "",
|
||||
location: locations.cells[0],
|
||||
entryPoint: locations.cells[1],
|
||||
zone: {
|
||||
dimensions: {
|
||||
x: 4000,
|
||||
y: 4000,
|
||||
z: 4000
|
||||
},
|
||||
instances: [{
|
||||
model: "Cell",
|
||||
light: {
|
||||
r: 255,
|
||||
g: 200,
|
||||
b: 200
|
||||
},
|
||||
intensity: 1.1,
|
||||
ambient: 0.7,
|
||||
sun: true,
|
||||
skybox: "cells_skybox_cross"
|
||||
},
|
||||
instances: [{
|
||||
model: "Cell",
|
||||
dimensions: {
|
||||
x: 550,
|
||||
y: 620,
|
||||
z: 550
|
||||
},
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 500,
|
||||
number: 10,
|
||||
userData: JSON.stringify({
|
||||
entryPoint: locations.cellLayout[1],
|
||||
target: locations.cellLayout[1],
|
||||
location: locations.cellLayout[1],
|
||||
baseURL: baseLocation
|
||||
}),
|
||||
script: "zoom.js?" + version,
|
||||
visible: true
|
||||
}],
|
||||
boundary: {
|
||||
radius: locations.cells[2],
|
||||
center: locations.cells[0],
|
||||
location: locations.cellLayout[1],
|
||||
target: locations.cellLayout[0]
|
||||
}
|
||||
}, {
|
||||
name: "CellLayout",
|
||||
objects: cellLayout,
|
||||
location: locations.cellLayout[0],
|
||||
entryPoint: locations.cellLayout[1],
|
||||
zone: {
|
||||
dimensions: {
|
||||
x: 4000,
|
||||
y: 4000,
|
||||
z: 4000
|
||||
},
|
||||
light: {
|
||||
r: 247,
|
||||
g: 233,
|
||||
b: 220
|
||||
},
|
||||
intensity: 2.3,
|
||||
ambient: 0.7,
|
||||
sun: true,
|
||||
skybox: "cosmos_skybox_blurred"
|
||||
},
|
||||
instances: [{
|
||||
model: "translation",
|
||||
dimensions: {
|
||||
x: 550,
|
||||
y: 620,
|
||||
z: 550
|
||||
x: 10,
|
||||
y: 16,
|
||||
z: 10
|
||||
},
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 500,
|
||||
number: 10,
|
||||
radius: 300,
|
||||
number: 7,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
},
|
||||
target: locations.ribosome[1],
|
||||
location: locations.ribosome[0],
|
||||
baseURL: baseLocation
|
||||
}),
|
||||
script: "zoom.js?" + version,
|
||||
visible: true
|
||||
}, {
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 60,
|
||||
y: 60,
|
||||
z: 60
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1000,
|
||||
number: 22,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "moveRandomly.js?" + version,
|
||||
visible: true
|
||||
}],
|
||||
boundary: {
|
||||
radius: locations.cells[2],
|
||||
center: locations.cells[0],
|
||||
location: locations.cellLayout[1],
|
||||
target: locations.cellLayout[0]
|
||||
}
|
||||
}, {
|
||||
name: "CellLayout",
|
||||
objects: cellLayout,
|
||||
location: locations.cellLayout[0],
|
||||
entryPoint: locations.cellLayout[1],
|
||||
zone: {
|
||||
}, { //golgi vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 4000,
|
||||
y: 4000,
|
||||
z: 4000
|
||||
},
|
||||
light: {
|
||||
r: 247,
|
||||
g: 233,
|
||||
b: 220
|
||||
},
|
||||
intensity: 2.3,
|
||||
ambient: 0.7,
|
||||
sun: true,
|
||||
skybox: "cosmos_skybox_blurred"
|
||||
},
|
||||
instances: [{
|
||||
model: "translation",
|
||||
dimensions: {
|
||||
x: 10,
|
||||
y: 16,
|
||||
z: 10
|
||||
},
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 300,
|
||||
number: 15,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
},
|
||||
target: locations.ribosome[1],
|
||||
location: locations.ribosome[0],
|
||||
baseURL: baseLocation
|
||||
}),
|
||||
script: "zoom.js?" + version,
|
||||
visible: true
|
||||
}, {
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 60,
|
||||
y: 60,
|
||||
z: 60
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1000,
|
||||
number: 45,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "moveRandomly.js?" + version,
|
||||
visible: true
|
||||
}, { //golgi vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: -319,
|
||||
y: 66,
|
||||
z: 976
|
||||
},
|
||||
radius: 140,
|
||||
number: 20,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}, { //golgi vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 15,
|
||||
y: 15,
|
||||
z: 15
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: -319,
|
||||
y: 66,
|
||||
z: 976
|
||||
},
|
||||
radius: 115,
|
||||
number: 15,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "moveRandomly.js?" + version,
|
||||
visible: true
|
||||
}, {
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 50,
|
||||
y: 50,
|
||||
z: 50
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 600,
|
||||
number: 30,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}, { //outer vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 60,
|
||||
y: 60,
|
||||
z: 60
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1600,
|
||||
number: 45,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}, { //outer vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 40,
|
||||
y: 40,
|
||||
z: 40
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1400,
|
||||
number: 45,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "moveRandomly.js?" + version,
|
||||
visible: true
|
||||
}, { //outer vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 80,
|
||||
y: 80,
|
||||
z: 80
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1800,
|
||||
number: 45,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "moveRandomly.js?" + version,
|
||||
visible: true
|
||||
},
|
||||
// {//wigglies
|
||||
// model:"wiggly",
|
||||
// dimensions:{x:320,y:40,z:160},
|
||||
// randomSize: 10,
|
||||
// offset:{x:0,y:0,z:0},
|
||||
// radius:1800,
|
||||
// number:50,
|
||||
// userData:"",
|
||||
// script:"moveRandomly",
|
||||
// visible:true
|
||||
// },
|
||||
//// {//wigglies
|
||||
// model:"wiggly",
|
||||
// dimensions:{x:640,y:80,z:320},
|
||||
// randomSize: 10,
|
||||
// offset:{x:0,y:0,z:0},
|
||||
// radius:2100,
|
||||
// number:50,
|
||||
// userData:"",
|
||||
// script:"moveRandomly",
|
||||
// visible:true
|
||||
// },
|
||||
{
|
||||
model: "hexokinase",
|
||||
dimensions: {
|
||||
x: 3,
|
||||
y: 4,
|
||||
z: 3
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 236,
|
||||
y: 8,
|
||||
z: 771
|
||||
},
|
||||
radius: 80,
|
||||
number: 15,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
},
|
||||
target: locations.hexokinase[1],
|
||||
location: locations.hexokinase[0],
|
||||
baseURL: baseLocation
|
||||
}),
|
||||
script: "zoom.js?" + version,
|
||||
visible: true
|
||||
}, {
|
||||
model: "pfructo_kinase",
|
||||
dimensions: {
|
||||
x: 3,
|
||||
y: 4,
|
||||
z: 3
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 236,
|
||||
y: 8,
|
||||
z: 771
|
||||
},
|
||||
radius: 60,
|
||||
number: 15,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}, {
|
||||
model: "glucose_isomerase",
|
||||
dimensions: {
|
||||
x: 3,
|
||||
y: 4,
|
||||
z: 3
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 236,
|
||||
y: 8,
|
||||
z: 771
|
||||
},
|
||||
radius: 70,
|
||||
number: 15,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}
|
||||
// {
|
||||
// model:"NPC",
|
||||
// dimensions:{x:20,y:20,z:20},
|
||||
// randomSize: 10,
|
||||
// offset:{x:208.593693,y:6.113100222,z:153.3202277},
|
||||
// radius:520,
|
||||
// number:25,
|
||||
// userData: "",
|
||||
// script:"",
|
||||
// visible:true
|
||||
// }
|
||||
|
||||
|
||||
],
|
||||
boundary: {
|
||||
radius: locations.cellLayout[2],
|
||||
center: locations.cellLayout[0],
|
||||
location: locations.cells[1],
|
||||
target: locations.cells[0]
|
||||
}
|
||||
}, {
|
||||
name: "Ribosome",
|
||||
objects: "",
|
||||
location: locations.ribosome[0],
|
||||
entryPoint: locations.ribosome[1],
|
||||
zone: {
|
||||
dimensions: {
|
||||
x: 4000,
|
||||
y: 4000,
|
||||
z: 4000
|
||||
},
|
||||
light: {
|
||||
r: 250,
|
||||
g: 185,
|
||||
b: 182
|
||||
},
|
||||
intensity: 0.6,
|
||||
ambient: 2.9,
|
||||
sun: true,
|
||||
skybox: "ribosome_skybox"
|
||||
},
|
||||
instances: [{
|
||||
model: "translation_highres",
|
||||
dimensions: {
|
||||
x: 500,
|
||||
y: 500,
|
||||
z: 200
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
x: -319,
|
||||
y: 66,
|
||||
z: 976
|
||||
},
|
||||
radius: 1,
|
||||
number: 1,
|
||||
radius: 140,
|
||||
number: 10,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
|
@ -478,48 +218,43 @@ var scenes = [
|
|||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}],
|
||||
boundary: {
|
||||
radius: locations.ribosome[2],
|
||||
center: locations.ribosome[0],
|
||||
location: locations.translation[1],
|
||||
target: locations.translation[0]
|
||||
}
|
||||
}, {
|
||||
name: "Hexokinase",
|
||||
objects: "",
|
||||
location: locations.hexokinase[0],
|
||||
entryPoint: locations.hexokinase[1],
|
||||
zone: {
|
||||
}, { //golgi vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 4000,
|
||||
y: 4000,
|
||||
z: 4000
|
||||
x: 15,
|
||||
y: 15,
|
||||
z: 15
|
||||
},
|
||||
light: {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: -319,
|
||||
y: 66,
|
||||
z: 976
|
||||
},
|
||||
intensity: 0.6,
|
||||
ambient: 0.6,
|
||||
sun: true,
|
||||
skybox: "hexokinase_skybox"
|
||||
},
|
||||
instances: [{
|
||||
model: "hexokinase_highres",
|
||||
radius: 115,
|
||||
number: 7,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "moveRandomly.js?" + version,
|
||||
visible: true
|
||||
}, {
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 600,
|
||||
y: 600,
|
||||
z: 600
|
||||
x: 50,
|
||||
y: 50,
|
||||
z: 50
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1,
|
||||
number: 1,
|
||||
radius: 600,
|
||||
number: 15,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
|
@ -527,15 +262,288 @@ var scenes = [
|
|||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}],
|
||||
boundary: {
|
||||
radius: locations.hexokinase[2],
|
||||
center: locations.hexokinase[0],
|
||||
location: locations.mitochondria[1],
|
||||
target: locations.mitochondria[0]
|
||||
}, { //outer vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 60,
|
||||
y: 60,
|
||||
z: 60
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1600,
|
||||
number: 22,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}, { //outer vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 40,
|
||||
y: 40,
|
||||
z: 40
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1400,
|
||||
number: 22,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "moveRandomly.js?" + version,
|
||||
visible: true
|
||||
}, { //outer vesicles
|
||||
model: "vesicle",
|
||||
dimensions: {
|
||||
x: 80,
|
||||
y: 80,
|
||||
z: 80
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1800,
|
||||
number: 22,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "moveRandomly.js?" + version,
|
||||
visible: true
|
||||
},
|
||||
// {//wigglies
|
||||
// model:"wiggly",
|
||||
// dimensions:{x:320,y:40,z:160},
|
||||
// randomSize: 10,
|
||||
// offset:{x:0,y:0,z:0},
|
||||
// radius:1800,
|
||||
// number:50,
|
||||
// userData:"",
|
||||
// script:"moveRandomly",
|
||||
// visible:true
|
||||
// },
|
||||
//// {//wigglies
|
||||
// model:"wiggly",
|
||||
// dimensions:{x:640,y:80,z:320},
|
||||
// randomSize: 10,
|
||||
// offset:{x:0,y:0,z:0},
|
||||
// radius:2100,
|
||||
// number:50,
|
||||
// userData:"",
|
||||
// script:"moveRandomly",
|
||||
// visible:true
|
||||
// },
|
||||
{
|
||||
model: "hexokinase",
|
||||
dimensions: {
|
||||
x: 3,
|
||||
y: 4,
|
||||
z: 3
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 236,
|
||||
y: 8,
|
||||
z: 771
|
||||
},
|
||||
radius: 80,
|
||||
number: 7,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
},
|
||||
target: locations.hexokinase[1],
|
||||
location: locations.hexokinase[0],
|
||||
baseURL: baseLocation
|
||||
}),
|
||||
script: "zoom.js?" + version,
|
||||
visible: true
|
||||
}, {
|
||||
model: "pfructo_kinase",
|
||||
dimensions: {
|
||||
x: 3,
|
||||
y: 4,
|
||||
z: 3
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 236,
|
||||
y: 8,
|
||||
z: 771
|
||||
},
|
||||
radius: 60,
|
||||
number: 7,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
},
|
||||
target: locations.hexokinase[1],
|
||||
location: locations.hexokinase[0],
|
||||
}),
|
||||
script: "zoom.js?" + version,
|
||||
visible: true
|
||||
}, {
|
||||
model: "glucose_isomerase",
|
||||
dimensions: {
|
||||
x: 3,
|
||||
y: 4,
|
||||
z: 3
|
||||
},
|
||||
randomSize: 10,
|
||||
offset: {
|
||||
x: 236,
|
||||
y: 8,
|
||||
z: 771
|
||||
},
|
||||
radius: 70,
|
||||
number: 7,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
},
|
||||
target: locations.hexokinase[1],
|
||||
location: locations.hexokinase[0],
|
||||
}),
|
||||
script: "zoom.js?" + version,
|
||||
visible: true
|
||||
}
|
||||
// {
|
||||
// model:"NPC",
|
||||
// dimensions:{x:20,y:20,z:20},
|
||||
// randomSize: 10,
|
||||
// offset:{x:208.593693,y:6.113100222,z:153.3202277},
|
||||
// radius:520,
|
||||
// number:25,
|
||||
// userData: "",
|
||||
// script:"",
|
||||
// visible:true
|
||||
// }
|
||||
|
||||
|
||||
],
|
||||
boundary: {
|
||||
radius: locations.cellLayout[2],
|
||||
center: locations.cellLayout[0],
|
||||
location: locations.cells[1],
|
||||
target: locations.cells[0]
|
||||
}
|
||||
];
|
||||
}, {
|
||||
name: "Ribosome",
|
||||
objects: "",
|
||||
location: locations.ribosome[0],
|
||||
entryPoint: locations.ribosome[1],
|
||||
zone: {
|
||||
dimensions: {
|
||||
x: 4000,
|
||||
y: 4000,
|
||||
z: 4000
|
||||
},
|
||||
light: {
|
||||
r: 250,
|
||||
g: 185,
|
||||
b: 182
|
||||
},
|
||||
intensity: 0.6,
|
||||
ambient: 2.9,
|
||||
sun: true,
|
||||
skybox: "ribosome_skybox"
|
||||
},
|
||||
instances: [{
|
||||
model: "translation_highres",
|
||||
dimensions: {
|
||||
x: 500,
|
||||
y: 500,
|
||||
z: 200
|
||||
},
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1,
|
||||
number: 1,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}],
|
||||
boundary: {
|
||||
radius: locations.ribosome[2],
|
||||
center: locations.ribosome[0],
|
||||
location: locations.translation[1],
|
||||
target: locations.translation[0]
|
||||
}
|
||||
}, {
|
||||
name: "Hexokinase",
|
||||
objects: "",
|
||||
location: locations.hexokinase[0],
|
||||
entryPoint: locations.hexokinase[1],
|
||||
zone: {
|
||||
dimensions: {
|
||||
x: 4000,
|
||||
y: 4000,
|
||||
z: 4000
|
||||
},
|
||||
light: {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255
|
||||
},
|
||||
intensity: 0.6,
|
||||
ambient: 0.6,
|
||||
sun: true,
|
||||
skybox: "hexokinase_skybox"
|
||||
},
|
||||
instances: [{
|
||||
model: "hexokinase_highres",
|
||||
dimensions: {
|
||||
x: 600,
|
||||
y: 600,
|
||||
z: 600
|
||||
},
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
radius: 1,
|
||||
number: 1,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
}),
|
||||
script: "",
|
||||
visible: true
|
||||
}],
|
||||
boundary: {
|
||||
radius: locations.hexokinase[2],
|
||||
center: locations.hexokinase[0],
|
||||
location: locations.mitochondria[1],
|
||||
target: locations.mitochondria[0]
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
function ImportScene(scene) {
|
||||
|
@ -594,8 +602,6 @@ function ImportScene(scene) {
|
|||
CreateInstances(scene);
|
||||
CreateBoundary(scene);
|
||||
|
||||
CreateBackgroundAudio(scene.name, scene.location, scene.dimensions);
|
||||
|
||||
// print("done " + scene.name);
|
||||
|
||||
}
|
||||
|
@ -637,12 +643,13 @@ function createLayoutLights() {
|
|||
})
|
||||
|
||||
}
|
||||
|
||||
function CreateNavigationButton(scene, number) {
|
||||
// print('NAV NAVIGATION CREATING NAV!!' +scene.name + " " + number)
|
||||
|
||||
|
||||
Entities.addEntity({
|
||||
type: "Sphere",
|
||||
type: "Box",
|
||||
name: scene.name + " navigation button",
|
||||
color: {
|
||||
red: 200,
|
||||
|
@ -650,9 +657,9 @@ function CreateNavigationButton(scene, number) {
|
|||
blue: 0
|
||||
},
|
||||
dimensions: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
z: 10
|
||||
x: 16000,
|
||||
y: 16000,
|
||||
z: 16000
|
||||
},
|
||||
visible: false,
|
||||
userData: JSON.stringify({
|
||||
|
@ -665,10 +672,13 @@ function CreateNavigationButton(scene, number) {
|
|||
grabbable: false
|
||||
}
|
||||
}),
|
||||
// position:{x:3000,y:13500,z:3000},
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
script: baseLocation + "Scripts/navigationButton.js?" + version,
|
||||
collisionless: true,
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -811,7 +821,7 @@ function CreateInstances(scene) {
|
|||
}, idBounds, 150);
|
||||
|
||||
}
|
||||
print('Script.clearTimeout SCRIPT AT CREATE ENTITY: ' + script)
|
||||
//print('SCRIPT AT CREATE ENTITY: ' + script)
|
||||
CreateEntity(scene.instances[i].model, position, rotation, scene.instances[i].dimensions, url, script, scene.instances[i].userData, scene.instances[i].visible);
|
||||
}
|
||||
}
|
||||
|
@ -845,27 +855,6 @@ function CreateIdentification(name, position, rotation, dimensions, showDistance
|
|||
|
||||
}
|
||||
|
||||
function CreateBackgroundAudio(name, position) {
|
||||
Entities.addEntity({
|
||||
type: "Sphere",
|
||||
name: "Location " + name + " background audio",
|
||||
dimensions: {
|
||||
x: 4000,
|
||||
y: 4000,
|
||||
z: 4000
|
||||
},
|
||||
position: position,
|
||||
visible: false,
|
||||
userData: JSON.stringify({
|
||||
name: name,
|
||||
baseURL: baseLocation
|
||||
}),
|
||||
script: baseLocation + "Scripts/playBackgroundAudio.js?" + version,
|
||||
collisionless: true,
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function getPointOnSphereOfRadius(radius, number, totalNumber) {
|
||||
|
||||
|
@ -890,7 +879,7 @@ function getPointOnSphereOfRadius(radius, number, totalNumber) {
|
|||
// print("inc " + inc + " off " + off + " y " + y + " r " + r + " phi " + phi);
|
||||
|
||||
if (isNaN(r)) {
|
||||
print("r is not a number");
|
||||
//print("r is not a number");
|
||||
r = 1;
|
||||
}
|
||||
|
||||
|
@ -913,7 +902,7 @@ function CreateEntity(name, position, rotation, dimensions, url, script, userDat
|
|||
scriptLocation = baseLocation + "Scripts/" + script;
|
||||
}
|
||||
|
||||
print('Script.clearTimeout SCRIPT LOCATION IN CREATE ENTITY' + scriptLocation)
|
||||
//print(' SCRIPT LOCATION IN CREATE ENTITY' + scriptLocation)
|
||||
Entities.addEntity({
|
||||
type: "Model",
|
||||
name: name,
|
||||
|
|
Loading…
Reference in a new issue