mirror of
https://github.com/overte-org/overte.git
synced 2025-04-12 03:45:08 +02:00
Merge remote-tracking branch 'upstream/master' into 19598
Conflicts: interface/src/ui/FramelessDialog.h
This commit is contained in:
commit
45fdd00608
63 changed files with 1207 additions and 602 deletions
|
@ -21,12 +21,10 @@
|
|||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Systime.h"
|
||||
#include <math.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#endif //_WIN32
|
||||
|
||||
|
@ -389,9 +387,8 @@ void AudioMixer::run() {
|
|||
nodeList->linkedDataCreateCallback = attachNewBufferToNode;
|
||||
|
||||
int nextFrame = 0;
|
||||
timeval startTime;
|
||||
|
||||
gettimeofday(&startTime, NULL);
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
char* clientMixBuffer = new char[NETWORK_BUFFER_LENGTH_BYTES_STEREO
|
||||
+ numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio)];
|
||||
|
@ -490,7 +487,7 @@ void AudioMixer::run() {
|
|||
break;
|
||||
}
|
||||
|
||||
usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
usecToSleep = (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - timer.nsecsElapsed() / 1000; // ns to us
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "AssignmentClientMonitor.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
|
|
@ -449,7 +449,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
if (nodeInterestList.size() > 0) {
|
||||
|
||||
DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
||||
unsigned int dataMTU = dtlsSession ? gnutls_dtls_get_data_mtu(*dtlsSession->getGnuTLSSession()) : MAX_PACKET_SIZE;
|
||||
int dataMTU = dtlsSession ? (int)gnutls_dtls_get_data_mtu(*dtlsSession->getGnuTLSSession()) : MAX_PACKET_SIZE;
|
||||
|
||||
// if the node has any interest types, send back those nodes as well
|
||||
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <Logging.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "DomainServer.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
|
|
@ -1088,7 +1088,6 @@ function keyPressEvent(event) {
|
|||
red: colors[whichColor].red,
|
||||
green: colors[whichColor].green,
|
||||
blue: colors[whichColor].blue };
|
||||
Voxels.eraseVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s);
|
||||
Voxels.setVoxel(newVoxel.x, newVoxel.y, newVoxel.z, newVoxel.s, newVoxel.red, newVoxel.green, newVoxel.blue);
|
||||
setAudioPosition();
|
||||
initialVoxelSound.playRandom();
|
||||
|
@ -1394,7 +1393,6 @@ function checkControllers() {
|
|||
if (Vec3.length(Vec3.subtract(fingerTipPosition,lastFingerAddVoxel)) > (FINGERTIP_VOXEL_SIZE / 2)) {
|
||||
newColor = { red: colors[whichColor].red, green: colors[whichColor].green, blue: colors[whichColor].blue };
|
||||
|
||||
Voxels.eraseVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE);
|
||||
Voxels.setVoxel(fingerTipPosition.x, fingerTipPosition.y, fingerTipPosition.z, FINGERTIP_VOXEL_SIZE,
|
||||
newColor.red, newColor.green, newColor.blue);
|
||||
|
||||
|
|
142
examples/swissArmyJetpack.js
Normal file
142
examples/swissArmyJetpack.js
Normal file
|
@ -0,0 +1,142 @@
|
|||
//
|
||||
// swissArmyJetpack.js
|
||||
// examples
|
||||
//
|
||||
// Created by Andrew Meadows 2014.04.24
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This is a work in progress. It will eventually be able to move the avatar around,
|
||||
// toggle collision groups, modify avatar movement options, and other stuff (maybe trigger animations).
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var numberOfButtons = 3;
|
||||
|
||||
var enabledColors = new Array();
|
||||
enabledColors[0] = { red: 255, green: 0, blue: 0};
|
||||
enabledColors[1] = { red: 0, green: 255, blue: 0};
|
||||
enabledColors[2] = { red: 0, green: 0, blue: 255};
|
||||
|
||||
var disabledColors = new Array();
|
||||
disabledColors[0] = { red: 90, green: 75, blue: 75};
|
||||
disabledColors[1] = { red: 75, green: 90, blue: 75};
|
||||
disabledColors[2] = { red: 75, green: 90, blue: 90};
|
||||
|
||||
var buttons = new Array();
|
||||
var labels = new Array();
|
||||
|
||||
var labelContents = new Array();
|
||||
labelContents[0] = "Collide with Avatars";
|
||||
labelContents[1] = "Collide with Voxels";
|
||||
labelContents[2] = "Collide with Particles";
|
||||
var groupBits = 0;
|
||||
|
||||
var buttonStates = new Array();
|
||||
|
||||
var disabledOffsetT = 0;
|
||||
var enabledOffsetT = 55;
|
||||
|
||||
var buttonX = 50;
|
||||
var buttonY = 200;
|
||||
var buttonWidth = 30;
|
||||
var buttonHeight = 54;
|
||||
var textX = buttonX + buttonWidth + 10;
|
||||
|
||||
for (i = 0; i < numberOfButtons; i++) {
|
||||
var offsetS = 12
|
||||
var offsetT = disabledOffsetT;
|
||||
|
||||
buttons[i] = Overlays.addOverlay("image", {
|
||||
//x: buttonX + (buttonWidth * i),
|
||||
x: buttonX,
|
||||
y: buttonY + (buttonHeight * i),
|
||||
width: buttonWidth,
|
||||
height: buttonHeight,
|
||||
subImage: { x: offsetS, y: offsetT, width: buttonWidth, height: buttonHeight },
|
||||
imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg",
|
||||
color: disabledColors[i],
|
||||
alpha: 1,
|
||||
});
|
||||
|
||||
labels[i] = Overlays.addOverlay("text", {
|
||||
x: textX,
|
||||
y: buttonY + (buttonHeight * i) + 12,
|
||||
width: 150,
|
||||
height: 50,
|
||||
color: { red: 0, green: 0, blue: 0},
|
||||
textColor: { red: 255, green: 0, blue: 0},
|
||||
topMargin: 4,
|
||||
leftMargin: 4,
|
||||
text: labelContents[i]
|
||||
});
|
||||
|
||||
buttonStates[i] = false;
|
||||
}
|
||||
|
||||
function updateButton(i, enabled) {
|
||||
var offsetY = disabledOffsetT;
|
||||
var buttonColor = disabledColors[i];
|
||||
groupBits
|
||||
if (enabled) {
|
||||
offsetY = enabledOffsetT;
|
||||
buttonColor = enabledColors[i];
|
||||
if (i == 0) {
|
||||
groupBits |= COLLISION_GROUP_AVATARS;
|
||||
} else if (i == 1) {
|
||||
groupBits |= COLLISION_GROUP_VOXELS;
|
||||
} else if (i == 2) {
|
||||
groupBits |= COLLISION_GROUP_PARTICLES;
|
||||
}
|
||||
} else {
|
||||
if (i == 0) {
|
||||
groupBits &= ~COLLISION_GROUP_AVATARS;
|
||||
} else if (i == 1) {
|
||||
groupBits &= ~COLLISION_GROUP_VOXELS;
|
||||
} else if (i == 2) {
|
||||
groupBits &= ~COLLISION_GROUP_PARTICLES;
|
||||
}
|
||||
}
|
||||
MyAvatar.collisionGroups = groupBits;
|
||||
|
||||
Overlays.editOverlay(buttons[i], { subImage: { y: offsetY } } );
|
||||
Overlays.editOverlay(buttons[i], { color: buttonColor } );
|
||||
buttonStates[i] = enabled;
|
||||
}
|
||||
|
||||
// When our script shuts down, we should clean up all of our overlays
|
||||
function scriptEnding() {
|
||||
for (i = 0; i < numberOfButtons; i++) {
|
||||
print("adebug deleting overlay " + i);
|
||||
Overlays.deleteOverlay(buttons[i]);
|
||||
Overlays.deleteOverlay(labels[i]);
|
||||
}
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
||||
// Our update() function is called at approximately 60fps, and we will use it to animate our various overlays
|
||||
function update(deltaTime) {
|
||||
if (groupBits != MyAvatar.collisionGroups) {
|
||||
groupBits = MyAvatar.collisionGroups;
|
||||
updateButton(0, groupBits & COLLISION_GROUP_AVATARS);
|
||||
updateButton(1, groupBits & COLLISION_GROUP_VOXELS);
|
||||
updateButton(2, groupBits & COLLISION_GROUP_PARTICLES);
|
||||
}
|
||||
}
|
||||
Script.update.connect(update);
|
||||
|
||||
|
||||
// we also handle click detection in our mousePressEvent()
|
||||
function mousePressEvent(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
for (i = 0; i < numberOfButtons; i++) {
|
||||
if (clickedOverlay == buttons[i]) {
|
||||
var enabled = !(buttonStates[i]);
|
||||
updateButton(i, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
|
|
@ -9,10 +9,6 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifdef WIN32
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
@ -134,7 +130,7 @@ QString& Application::resourcesPath() {
|
|||
return staticResourcePath;
|
||||
}
|
||||
|
||||
Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||
Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||
QApplication(argc, argv),
|
||||
_window(new QMainWindow(desktop())),
|
||||
_glWidget(new GLCanvas()),
|
||||
|
@ -507,7 +503,7 @@ void Application::initializeGL() {
|
|||
_idleLoopStdev.reset();
|
||||
|
||||
if (_justStarted) {
|
||||
float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime)) / 1000000.0;
|
||||
float startupTime = (float)_applicationStartupTime.elapsed() / 1000.0;
|
||||
_justStarted = false;
|
||||
qDebug("Startup time: %4.2f seconds.", startupTime);
|
||||
const char LOGSTASH_INTERFACE_START_TIME_KEY[] = "interface-start-time";
|
||||
|
@ -861,7 +857,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
case Qt::Key_G:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Gravity);
|
||||
Menu::getInstance()->triggerOption(MenuOption::ObeyGravity);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1222,7 +1218,6 @@ void Application::touchEndEvent(QTouchEvent* event) {
|
|||
if (_controllerScriptingInterface.isTouchCaptured()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// put any application specific touch behavior below here..
|
||||
_touchDragStartedAvgX = _touchAvgX;
|
||||
_touchDragStartedAvgY = _touchAvgY;
|
||||
|
@ -1276,21 +1271,21 @@ void Application::sendPingPackets() {
|
|||
|
||||
// Every second, check the frame rates and other stuff
|
||||
void Application::timer() {
|
||||
gettimeofday(&_timerEnd, NULL);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
||||
sendPingPackets();
|
||||
}
|
||||
|
||||
float diffTime = (float)_timerStart.nsecsElapsed() / 1000000000.0f;
|
||||
|
||||
_fps = (float)_frameCount / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
|
||||
_fps = (float)_frameCount / diffTime;
|
||||
|
||||
_packetsPerSecond = (float) _datagramProcessor.getPacketCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
|
||||
_bytesPerSecond = (float) _datagramProcessor.getByteCount() / ((float)diffclock(&_timerStart, &_timerEnd) / 1000.f);
|
||||
_packetsPerSecond = (float) _datagramProcessor.getPacketCount() / diffTime;
|
||||
_bytesPerSecond = (float) _datagramProcessor.getByteCount() / diffTime;
|
||||
_frameCount = 0;
|
||||
|
||||
_datagramProcessor.resetCounters();
|
||||
|
||||
gettimeofday(&_timerStart, NULL);
|
||||
_timerStart.start();
|
||||
|
||||
// ask the node list to check in with the domain server
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
|
@ -1303,13 +1298,11 @@ void Application::idle() {
|
|||
bool showWarnings = getLogger()->extraDebugging();
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()");
|
||||
|
||||
timeval check;
|
||||
gettimeofday(&check, NULL);
|
||||
|
||||
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran
|
||||
|
||||
double timeSinceLastUpdate = diffclock(&_lastTimeUpdated, &check);
|
||||
double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0;
|
||||
if (timeSinceLastUpdate > IDLE_SIMULATE_MSECS) {
|
||||
_lastTimeUpdated.start();
|
||||
{
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
|
||||
const float BIGGEST_DELTA_TIME_SECS = 0.25f;
|
||||
|
@ -1321,7 +1314,6 @@ void Application::idle() {
|
|||
}
|
||||
{
|
||||
PerformanceWarning warn(showWarnings, "Application::idle()... rest of it");
|
||||
_lastTimeUpdated = check;
|
||||
_idleLoopStdev.addValue(timeSinceLastUpdate);
|
||||
|
||||
// Record standard deviation and reset counter if needed
|
||||
|
@ -1637,8 +1629,8 @@ void Application::init() {
|
|||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
gettimeofday(&_timerStart, NULL);
|
||||
gettimeofday(&_lastTimeUpdated, NULL);
|
||||
_timerStart.start();
|
||||
_lastTimeUpdated.start();
|
||||
|
||||
Menu::getInstance()->loadSettings();
|
||||
if (Menu::getInstance()->getAudioJitterBufferSamples() != 0) {
|
||||
|
@ -3528,35 +3520,30 @@ void Application::parseVersionXml() {
|
|||
QString operatingSystem("ubuntu");
|
||||
#endif
|
||||
|
||||
QString releaseDate;
|
||||
QString releaseNotes;
|
||||
QString latestVersion;
|
||||
QUrl downloadUrl;
|
||||
QString releaseNotes("Unavailable");
|
||||
QObject* sender = QObject::sender();
|
||||
|
||||
QXmlStreamReader xml(qobject_cast<QNetworkReply*>(sender));
|
||||
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
QXmlStreamReader::TokenType token = xml.readNext();
|
||||
|
||||
if (token == QXmlStreamReader::StartElement) {
|
||||
if (xml.name() == "ReleaseDate") {
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) {
|
||||
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == operatingSystem)) {
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "version") {
|
||||
xml.readNext();
|
||||
latestVersion = xml.text().toString();
|
||||
}
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "url") {
|
||||
xml.readNext();
|
||||
downloadUrl = QUrl(xml.text().toString());
|
||||
}
|
||||
xml.readNext();
|
||||
releaseDate = xml.text().toString();
|
||||
}
|
||||
if (xml.name() == "ReleaseNotes") {
|
||||
xml.readNext();
|
||||
releaseNotes = xml.text().toString();
|
||||
}
|
||||
if (xml.name() == "Version") {
|
||||
xml.readNext();
|
||||
latestVersion = xml.text().toString();
|
||||
}
|
||||
if (xml.name() == operatingSystem) {
|
||||
xml.readNext();
|
||||
downloadUrl = QUrl(xml.text().toString());
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
|
||||
if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) {
|
||||
new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <time.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QAction>
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
|
@ -117,7 +118,7 @@ public:
|
|||
static Application* getInstance() { return static_cast<Application*>(QCoreApplication::instance()); }
|
||||
static QString& resourcesPath();
|
||||
|
||||
Application(int& argc, char** argv, timeval &startup_time);
|
||||
Application(int& argc, char** argv, QElapsedTimer &startup_time);
|
||||
~Application();
|
||||
|
||||
void restoreSizeAndPosition();
|
||||
|
@ -392,9 +393,9 @@ private:
|
|||
|
||||
int _frameCount;
|
||||
float _fps;
|
||||
timeval _applicationStartupTime;
|
||||
timeval _timerStart, _timerEnd;
|
||||
timeval _lastTimeUpdated;
|
||||
QElapsedTimer _applicationStartupTime;
|
||||
QElapsedTimer _timerStart;
|
||||
QElapsedTimer _lastTimeUpdated;
|
||||
bool _justStarted;
|
||||
Stars _stars;
|
||||
|
||||
|
|
|
@ -480,7 +480,7 @@ void Audio::handleAudioInput() {
|
|||
float thisSample = 0;
|
||||
int samplesOverNoiseGate = 0;
|
||||
|
||||
const float NOISE_GATE_HEIGHT = 7.f;
|
||||
const float NOISE_GATE_HEIGHT = 7.0f;
|
||||
const int NOISE_GATE_WIDTH = 5;
|
||||
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
|
||||
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
|
||||
|
@ -490,7 +490,7 @@ void Audio::handleAudioInput() {
|
|||
//
|
||||
// Check clipping, adjust DC offset, and check if should open noise gate
|
||||
//
|
||||
float measuredDcOffset = 0.f;
|
||||
float measuredDcOffset = 0.0f;
|
||||
// Increment the time since the last clip
|
||||
if (_timeSinceLastClip >= 0.0f) {
|
||||
_timeSinceLastClip += (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float) SAMPLE_RATE;
|
||||
|
@ -500,7 +500,7 @@ void Audio::handleAudioInput() {
|
|||
measuredDcOffset += monoAudioSamples[i];
|
||||
monoAudioSamples[i] -= (int16_t) _dcOffset;
|
||||
thisSample = fabsf(monoAudioSamples[i]);
|
||||
if (thisSample >= (32767.f * CLIPPING_THRESHOLD)) {
|
||||
if (thisSample >= (32767.0f * CLIPPING_THRESHOLD)) {
|
||||
_timeSinceLastClip = 0.0f;
|
||||
}
|
||||
loudness += thisSample;
|
||||
|
@ -511,18 +511,18 @@ void Audio::handleAudioInput() {
|
|||
}
|
||||
|
||||
measuredDcOffset /= NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
|
||||
if (_dcOffset == 0.f) {
|
||||
if (_dcOffset == 0.0f) {
|
||||
// On first frame, copy over measured offset
|
||||
_dcOffset = measuredDcOffset;
|
||||
} else {
|
||||
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.f - DC_OFFSET_AVERAGING) * measuredDcOffset;
|
||||
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
|
||||
}
|
||||
|
||||
// Add tone injection if enabled
|
||||
const float TONE_FREQ = 220.f / SAMPLE_RATE * TWO_PI;
|
||||
const float QUARTER_VOLUME = 8192.f;
|
||||
const float TONE_FREQ = 220.0f / SAMPLE_RATE * TWO_PI;
|
||||
const float QUARTER_VOLUME = 8192.0f;
|
||||
if (_toneInjectionEnabled) {
|
||||
loudness = 0.f;
|
||||
loudness = 0.0f;
|
||||
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
|
||||
monoAudioSamples[i] = QUARTER_VOLUME * sinf(TONE_FREQ * (float)(i + _proceduralEffectSample));
|
||||
loudness += fabsf(monoAudioSamples[i]);
|
||||
|
@ -532,7 +532,7 @@ void Audio::handleAudioInput() {
|
|||
|
||||
// If Noise Gate is enabled, check and turn the gate on and off
|
||||
if (!_toneInjectionEnabled && _noiseGateEnabled) {
|
||||
float averageOfAllSampleFrames = 0.f;
|
||||
float averageOfAllSampleFrames = 0.0f;
|
||||
_noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness;
|
||||
if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
|
||||
float smallestSample = FLT_MAX;
|
||||
|
@ -643,13 +643,12 @@ void Audio::handleAudioInput() {
|
|||
void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
||||
const int NUM_INITIAL_PACKETS_DISCARD = 3;
|
||||
const int STANDARD_DEVIATION_SAMPLE_COUNT = 500;
|
||||
|
||||
timeval currentReceiveTime;
|
||||
gettimeofday(¤tReceiveTime, NULL);
|
||||
|
||||
_totalPacketsReceived++;
|
||||
|
||||
double timeDiff = diffclock(&_lastReceiveTime, ¤tReceiveTime);
|
||||
|
||||
|
||||
double timeDiff = (double)_timeSinceLastReceived.nsecsElapsed() / 1000000.0; // ns to ms
|
||||
_timeSinceLastReceived.start();
|
||||
|
||||
// Discard first few received packets for computing jitter (often they pile up on start)
|
||||
if (_totalPacketsReceived > NUM_INITIAL_PACKETS_DISCARD) {
|
||||
_stdev.addValue(timeDiff);
|
||||
|
@ -660,9 +659,9 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
|||
_stdev.reset();
|
||||
// Set jitter buffer to be a multiple of the measured standard deviation
|
||||
const int MAX_JITTER_BUFFER_SAMPLES = _ringBuffer.getSampleCapacity() / 2;
|
||||
const float NUM_STANDARD_DEVIATIONS = 3.f;
|
||||
const float NUM_STANDARD_DEVIATIONS = 3.0f;
|
||||
if (Menu::getInstance()->getAudioJitterBufferSamples() == 0) {
|
||||
float newJitterBufferSamples = (NUM_STANDARD_DEVIATIONS * _measuredJitter) / 1000.f * SAMPLE_RATE;
|
||||
float newJitterBufferSamples = (NUM_STANDARD_DEVIATIONS * _measuredJitter) / 1000.0f * SAMPLE_RATE;
|
||||
setJitterBufferSamples(glm::clamp((int)newJitterBufferSamples, 0, MAX_JITTER_BUFFER_SAMPLES));
|
||||
}
|
||||
}
|
||||
|
@ -673,8 +672,6 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
|
|||
}
|
||||
|
||||
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO).updateValue(audioByteArray.size());
|
||||
|
||||
_lastReceiveTime = currentReceiveTime;
|
||||
}
|
||||
|
||||
// NOTE: numSamples is the total number of single channel samples, since callers will always call this with stereo
|
||||
|
@ -903,10 +900,10 @@ void Audio::toggleAudioSpatialProcessing() {
|
|||
void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) {
|
||||
float sample;
|
||||
const float COLLISION_SOUND_CUTOFF_LEVEL = 0.01f;
|
||||
const float COLLISION_SOUND_MAX_VOLUME = 1000.f;
|
||||
const float COLLISION_SOUND_MAX_VOLUME = 1000.0f;
|
||||
const float UP_MAJOR_FIFTH = powf(1.5f, 4.0f);
|
||||
const float DOWN_TWO_OCTAVES = 4.f;
|
||||
const float DOWN_FOUR_OCTAVES = 16.f;
|
||||
const float DOWN_TWO_OCTAVES = 4.0f;
|
||||
const float DOWN_FOUR_OCTAVES = 16.0f;
|
||||
float t;
|
||||
if (_collisionSoundMagnitude > COLLISION_SOUND_CUTOFF_LEVEL) {
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
|
@ -936,12 +933,12 @@ void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) {
|
|||
_proceduralEffectSample += numSamples;
|
||||
|
||||
// Add a drum sound
|
||||
const float MAX_VOLUME = 32000.f;
|
||||
const float MAX_DURATION = 2.f;
|
||||
const float MAX_VOLUME = 32000.0f;
|
||||
const float MAX_DURATION = 2.0f;
|
||||
const float MIN_AUDIBLE_VOLUME = 0.001f;
|
||||
const float NOISE_MAGNITUDE = 0.02f;
|
||||
float frequency = (_drumSoundFrequency / SAMPLE_RATE) * TWO_PI;
|
||||
if (_drumSoundVolume > 0.f) {
|
||||
if (_drumSoundVolume > 0.0f) {
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
t = (float) _drumSoundSample + (float) i;
|
||||
sample = sinf(t * frequency);
|
||||
|
@ -961,12 +958,12 @@ void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) {
|
|||
_localProceduralSamples[i] = glm::clamp(_localProceduralSamples[i] + collisionSample,
|
||||
MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
|
||||
|
||||
_drumSoundVolume *= (1.f - _drumSoundDecay);
|
||||
_drumSoundVolume *= (1.0f - _drumSoundDecay);
|
||||
}
|
||||
_drumSoundSample += numSamples;
|
||||
_drumSoundDuration = glm::clamp(_drumSoundDuration - (AUDIO_CALLBACK_MSECS / 1000.f), 0.f, MAX_DURATION);
|
||||
if (_drumSoundDuration == 0.f || (_drumSoundVolume < MIN_AUDIBLE_VOLUME)) {
|
||||
_drumSoundVolume = 0.f;
|
||||
_drumSoundDuration = glm::clamp(_drumSoundDuration - (AUDIO_CALLBACK_MSECS / 1000.0f), 0.0f, MAX_DURATION);
|
||||
if (_drumSoundDuration == 0.0f || (_drumSoundVolume < MIN_AUDIBLE_VOLUME)) {
|
||||
_drumSoundVolume = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -999,7 +996,7 @@ void Audio::renderToolBox(int x, int y, bool boxed) {
|
|||
|
||||
if (boxed) {
|
||||
|
||||
bool isClipping = ((getTimeSinceLastClip() > 0.f) && (getTimeSinceLastClip() < 1.f));
|
||||
bool isClipping = ((getTimeSinceLastClip() > 0.0f) && (getTimeSinceLastClip() < 1.0f));
|
||||
const int BOX_LEFT_PADDING = 5;
|
||||
const int BOX_TOP_PADDING = 10;
|
||||
const int BOX_WIDTH = 266;
|
||||
|
@ -1010,9 +1007,9 @@ void Audio::renderToolBox(int x, int y, bool boxed) {
|
|||
glBindTexture(GL_TEXTURE_2D, _boxTextureId);
|
||||
|
||||
if (isClipping) {
|
||||
glColor3f(1.f,0.f,0.f);
|
||||
glColor3f(1.0f, 0.0f, 0.0f);
|
||||
} else {
|
||||
glColor3f(.41f,.41f,.41f);
|
||||
glColor3f(0.41f, 0.41f, 0.41f);
|
||||
}
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
|
@ -1089,10 +1086,8 @@ void Audio::addBufferToScope(
|
|||
// Short int pointer to mapped samples in byte array
|
||||
int16_t* destination = (int16_t*) byteArray.data();
|
||||
|
||||
for (int i = 0; i < NETWORK_SAMPLES_PER_FRAME; i++) {
|
||||
|
||||
for (unsigned int i = 0; i < NETWORK_SAMPLES_PER_FRAME; i++) {
|
||||
sample = (float)source[i * sourceNumberOfChannels + sourceChannel];
|
||||
|
||||
if (sample > 0) {
|
||||
value = (int16_t)(multiplier * logf(sample));
|
||||
} else if (sample < 0) {
|
||||
|
@ -1100,7 +1095,6 @@ void Audio::addBufferToScope(
|
|||
} else {
|
||||
value = 0;
|
||||
}
|
||||
|
||||
destination[i + frameOffset] = value;
|
||||
}
|
||||
}
|
||||
|
@ -1271,13 +1265,13 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo)
|
|||
// setup a procedural audio output device
|
||||
_proceduralAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
||||
|
||||
gettimeofday(&_lastReceiveTime, NULL);
|
||||
_timeSinceLastReceived.start();
|
||||
|
||||
// setup spatial audio ringbuffer
|
||||
int numFrameSamples = _outputFormat.sampleRate() * _desiredOutputFormat.channelCount();
|
||||
_spatialAudioRingBuffer.resizeForFrameSize(numFrameSamples);
|
||||
_spatialAudioStart = _spatialAudioFinish = 0;
|
||||
|
||||
|
||||
supportedFormat = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,6 @@
|
|||
#ifndef hifi_Audio_h
|
||||
#define hifi_Audio_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
|
@ -23,6 +19,7 @@
|
|||
|
||||
#include <QAudio>
|
||||
#include <QAudioInput>
|
||||
#include <QElapsedTimer>
|
||||
#include <QGLWidget>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVector>
|
||||
|
@ -132,7 +129,7 @@ private:
|
|||
QString _outputAudioDeviceName;
|
||||
|
||||
StDev _stdev;
|
||||
timeval _lastReceiveTime;
|
||||
QElapsedTimer _timeSinceLastReceived;
|
||||
float _averagedLatency;
|
||||
float _measuredJitter;
|
||||
int16_t _jitterBufferSamples;
|
||||
|
|
|
@ -188,9 +188,9 @@ Menu::Menu() :
|
|||
QAction::PreferencesRole);
|
||||
|
||||
addDisabledActionAndSeparator(editMenu, "Physics");
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, false);
|
||||
|
||||
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyGravity, Qt::SHIFT | Qt::Key_G, true,
|
||||
avatar, SLOT(updateMotionBehaviorFlags()));
|
||||
|
||||
|
||||
addAvatarCollisionSubMenu(editMenu);
|
||||
|
@ -337,6 +337,8 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, true);
|
||||
|
||||
addDisabledActionAndSeparator(developerMenu, "Testing");
|
||||
|
||||
|
@ -507,7 +509,7 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
// MyAvatar caches some menu options, so we have to update them whenever we load settings.
|
||||
// TODO: cache more settings in MyAvatar that are checked with very high frequency.
|
||||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
myAvatar->updateCollisionFlags();
|
||||
myAvatar->updateCollisionGroups();
|
||||
|
||||
if (lockedSettings) {
|
||||
Application::getInstance()->unlockSettings();
|
||||
|
@ -1351,13 +1353,13 @@ void Menu::addAvatarCollisionSubMenu(QMenu* overMenu) {
|
|||
Application* appInstance = Application::getInstance();
|
||||
QObject* avatar = appInstance->getAvatar();
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithEnvironment,
|
||||
0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
0, false, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithAvatars,
|
||||
0, true, avatar, SLOT(updateCollisionFlags()));
|
||||
0, true, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithVoxels,
|
||||
0, false, avatar, SLOT(updateCollisionFlags()));
|
||||
0, false, avatar, SLOT(updateCollisionGroups()));
|
||||
addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithParticles,
|
||||
0, true, avatar, SLOT(updateCollisionFlags()));
|
||||
0, true, avatar, SLOT(updateCollisionGroups()));
|
||||
}
|
||||
|
||||
QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
|
||||
|
|
|
@ -257,6 +257,7 @@ private:
|
|||
|
||||
namespace MenuOption {
|
||||
const QString AboutApp = "About Interface";
|
||||
const QString AlignForearmsWithWrists = "Align Forearms with Wrists";
|
||||
const QString AmbientOcclusion = "Ambient Occlusion";
|
||||
const QString Atmosphere = "Atmosphere";
|
||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||
|
@ -314,7 +315,7 @@ namespace MenuOption {
|
|||
const QString GoTo = "Go To...";
|
||||
const QString GoToDomain = "Go To Domain...";
|
||||
const QString GoToLocation = "Go To Location...";
|
||||
const QString Gravity = "Use Gravity";
|
||||
const QString ObeyGravity = "Obey Gravity";
|
||||
const QString HandsCollideWithSelf = "Collide With Self";
|
||||
const QString HeadMouse = "Head Mouse";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
|
@ -355,6 +356,7 @@ namespace MenuOption {
|
|||
const QString SettingsImport = "Import Settings";
|
||||
const QString Shadows = "Shadows";
|
||||
const QString ShowCulledSharedFaces = "Show Culled Shared Voxel Faces";
|
||||
const QString ShowIKConstraints = "Show IK Constraints";
|
||||
const QString Stars = "Stars";
|
||||
const QString Stats = "Stats";
|
||||
const QString StopAllScripts = "Stop All Scripts";
|
||||
|
|
|
@ -172,14 +172,6 @@ void renderWorldBox() {
|
|||
|
||||
}
|
||||
|
||||
double diffclock(timeval *clock1,timeval *clock2)
|
||||
{
|
||||
double diffms = (clock2->tv_sec - clock1->tv_sec) * 1000.0;
|
||||
diffms += (clock2->tv_usec - clock1->tv_usec) / 1000.0; // us to ms
|
||||
|
||||
return diffms;
|
||||
}
|
||||
|
||||
// Return a random vector of average length 1
|
||||
const glm::vec3 randVector() {
|
||||
return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.f;
|
||||
|
@ -411,69 +403,63 @@ void runTimingTests() {
|
|||
int iResults[numTests];
|
||||
float fTest = 1.0;
|
||||
float fResults[numTests];
|
||||
timeval startTime, endTime;
|
||||
float elapsedMsecs;
|
||||
gettimeofday(&startTime, NULL);
|
||||
for (int i = 1; i < numTests; i++) {
|
||||
gettimeofday(&endTime, NULL);
|
||||
}
|
||||
elapsedMsecs = diffclock(&startTime, &endTime);
|
||||
qDebug("gettimeofday() usecs: %f", 1000.0f * elapsedMsecs / (float) numTests);
|
||||
QElapsedTimer startTime;
|
||||
startTime.start();
|
||||
float elapsedUsecs;
|
||||
|
||||
float NSEC_TO_USEC = 1.0f / 1000.0f;
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qDebug("QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs / (float) numTests);
|
||||
|
||||
// Random number generation
|
||||
gettimeofday(&startTime, NULL);
|
||||
startTime.start();
|
||||
for (int i = 1; i < numTests; i++) {
|
||||
iResults[i] = rand();
|
||||
}
|
||||
gettimeofday(&endTime, NULL);
|
||||
elapsedMsecs = diffclock(&startTime, &endTime);
|
||||
qDebug("rand() stored in array usecs: %f, first result:%d", 1000.0f * elapsedMsecs / (float) numTests, iResults[0]);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qDebug("rand() stored in array usecs: %f, first result:%d", elapsedUsecs / (float) numTests, iResults[0]);
|
||||
|
||||
// Random number generation using randFloat()
|
||||
gettimeofday(&startTime, NULL);
|
||||
startTime.start();
|
||||
for (int i = 1; i < numTests; i++) {
|
||||
fResults[i] = randFloat();
|
||||
}
|
||||
gettimeofday(&endTime, NULL);
|
||||
elapsedMsecs = diffclock(&startTime, &endTime);
|
||||
qDebug("randFloat() stored in array usecs: %f, first result: %f", 1000.0f * elapsedMsecs / (float) numTests, fResults[0]);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qDebug("randFloat() stored in array usecs: %f, first result: %f", elapsedUsecs / (float) numTests, fResults[0]);
|
||||
|
||||
// PowF function
|
||||
fTest = 1145323.2342f;
|
||||
gettimeofday(&startTime, NULL);
|
||||
startTime.start();
|
||||
for (int i = 1; i < numTests; i++) {
|
||||
fTest = powf(fTest, 0.5f);
|
||||
}
|
||||
gettimeofday(&endTime, NULL);
|
||||
elapsedMsecs = diffclock(&startTime, &endTime);
|
||||
qDebug("powf(f, 0.5) usecs: %f", 1000.0f * elapsedMsecs / (float) numTests);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qDebug("powf(f, 0.5) usecs: %f", elapsedUsecs / (float) numTests);
|
||||
|
||||
// Vector Math
|
||||
float distance;
|
||||
glm::vec3 pointA(randVector()), pointB(randVector());
|
||||
gettimeofday(&startTime, NULL);
|
||||
startTime.start();
|
||||
for (int i = 1; i < numTests; i++) {
|
||||
//glm::vec3 temp = pointA - pointB;
|
||||
//float distanceSquared = glm::dot(temp, temp);
|
||||
distance = glm::distance(pointA, pointB);
|
||||
}
|
||||
gettimeofday(&endTime, NULL);
|
||||
elapsedMsecs = diffclock(&startTime, &endTime);
|
||||
qDebug("vector math usecs: %f [%f msecs total for %d tests], last result:%f",
|
||||
1000.0f * elapsedMsecs / (float) numTests, elapsedMsecs, numTests, distance);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qDebug("vector math usecs: %f [%f usecs total for %d tests], last result:%f",
|
||||
elapsedUsecs / (float) numTests, elapsedUsecs, numTests, distance);
|
||||
|
||||
// Vec3 test
|
||||
glm::vec3 vecA(randVector()), vecB(randVector());
|
||||
float result;
|
||||
|
||||
gettimeofday(&startTime, NULL);
|
||||
|
||||
startTime.start();
|
||||
for (int i = 1; i < numTests; i++) {
|
||||
glm::vec3 temp = vecA-vecB;
|
||||
result = glm::dot(temp,temp);
|
||||
}
|
||||
gettimeofday(&endTime, NULL);
|
||||
elapsedMsecs = diffclock(&startTime, &endTime);
|
||||
qDebug("vec3 assign and dot() usecs: %f, last result:%f", 1000.0f * elapsedMsecs / (float) numTests, result);
|
||||
elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC;
|
||||
qDebug("vec3 assign and dot() usecs: %f, last result:%f", elapsedUsecs / (float) numTests, result);
|
||||
}
|
||||
|
||||
float loadSetting(QSettings* settings, const char* name, float defaultValue) {
|
||||
|
|
|
@ -12,12 +12,6 @@
|
|||
#ifndef hifi_Util_h
|
||||
#define hifi_Util_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Systime.h"
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <QSettings>
|
||||
|
@ -44,8 +38,6 @@ void drawVector(glm::vec3* vector);
|
|||
|
||||
void printVector(glm::vec3 vec);
|
||||
|
||||
double diffclock(timeval *clock1,timeval *clock2);
|
||||
|
||||
void renderCollisionOverlay(int width, int height, float magnitude, float red = 0, float blue = 0, float green = 0);
|
||||
|
||||
void renderOrientationDirections( glm::vec3 position, const glm::quat& orientation, float size );
|
||||
|
|
|
@ -47,16 +47,14 @@ Avatar::Avatar() :
|
|||
AvatarData(),
|
||||
_skeletonModel(this),
|
||||
_bodyYawDelta(0.0f),
|
||||
_mode(AVATAR_MODE_STANDING),
|
||||
_velocity(0.0f, 0.0f, 0.0f),
|
||||
_thrust(0.0f, 0.0f, 0.0f),
|
||||
_leanScale(0.5f),
|
||||
_scale(1.0f),
|
||||
_worldUpDirection(DEFAULT_UP_DIRECTION),
|
||||
_mouseRayOrigin(0.0f, 0.0f, 0.0f),
|
||||
_mouseRayDirection(0.0f, 0.0f, 0.0f),
|
||||
_moving(false),
|
||||
_collisionFlags(0),
|
||||
_collisionGroups(0),
|
||||
_initialized(false),
|
||||
_shouldRenderBillboard(true)
|
||||
{
|
||||
|
@ -138,20 +136,9 @@ void Avatar::simulate(float deltaTime) {
|
|||
head->simulate(deltaTime, false, _shouldRenderBillboard);
|
||||
}
|
||||
|
||||
// use speed and angular velocity to determine walking vs. standing
|
||||
float speed = glm::length(_velocity);
|
||||
if (speed + fabs(_bodyYawDelta) > 0.2) {
|
||||
_mode = AVATAR_MODE_WALKING;
|
||||
} else {
|
||||
_mode = AVATAR_MODE_INTERACTING;
|
||||
}
|
||||
|
||||
// update position by velocity, and subtract the change added earlier for gravity
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0, 0, 0);
|
||||
|
||||
// update animation for display name fade in/out
|
||||
if ( _displayNameTargetAlpha != _displayNameAlpha) {
|
||||
// the alpha function is
|
||||
|
@ -166,7 +153,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
// Fading in
|
||||
_displayNameAlpha = 1 - (1 - _displayNameAlpha) * coef;
|
||||
}
|
||||
_displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01? _displayNameTargetAlpha : _displayNameAlpha;
|
||||
_displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -563,7 +550,7 @@ bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList&
|
|||
}
|
||||
|
||||
bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
||||
if (_collisionFlags & COLLISION_GROUP_PARTICLES) {
|
||||
if (_collisionGroups & COLLISION_GROUP_PARTICLES) {
|
||||
return false;
|
||||
}
|
||||
bool collided = false;
|
||||
|
@ -753,19 +740,19 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2,
|
|||
glEnd();
|
||||
}
|
||||
|
||||
void Avatar::updateCollisionFlags() {
|
||||
_collisionFlags = 0;
|
||||
void Avatar::updateCollisionGroups() {
|
||||
_collisionGroups = 0;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithEnvironment)) {
|
||||
_collisionFlags |= COLLISION_GROUP_ENVIRONMENT;
|
||||
_collisionGroups |= COLLISION_GROUP_ENVIRONMENT;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) {
|
||||
_collisionFlags |= COLLISION_GROUP_AVATARS;
|
||||
_collisionGroups |= COLLISION_GROUP_AVATARS;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithVoxels)) {
|
||||
_collisionFlags |= COLLISION_GROUP_VOXELS;
|
||||
_collisionGroups |= COLLISION_GROUP_VOXELS;
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) {
|
||||
_collisionFlags |= COLLISION_GROUP_PARTICLES;
|
||||
_collisionGroups |= COLLISION_GROUP_PARTICLES;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,13 +46,6 @@ enum DriveKeys {
|
|||
MAX_DRIVE_KEYS
|
||||
};
|
||||
|
||||
enum AvatarMode {
|
||||
AVATAR_MODE_STANDING = 0,
|
||||
AVATAR_MODE_WALKING,
|
||||
AVATAR_MODE_INTERACTING,
|
||||
NUM_AVATAR_MODES
|
||||
};
|
||||
|
||||
enum ScreenTintLayer {
|
||||
SCREEN_TINT_BEFORE_LANDSCAPE = 0,
|
||||
SCREEN_TINT_BEFORE_AVATARS,
|
||||
|
@ -70,6 +63,7 @@ class Texture;
|
|||
|
||||
class Avatar : public AvatarData {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint32 collisionGroups READ getCollisionGroups WRITE setCollisionGroups)
|
||||
|
||||
public:
|
||||
Avatar();
|
||||
|
@ -155,8 +149,11 @@ public:
|
|||
virtual float getBoundingRadius() const;
|
||||
void updateShapePositions();
|
||||
|
||||
quint32 getCollisionGroups() const { return _collisionGroups; }
|
||||
virtual void setCollisionGroups(quint32 collisionGroups) { _collisionGroups = (collisionGroups & VALID_COLLISION_GROUPS); }
|
||||
|
||||
public slots:
|
||||
void updateCollisionFlags();
|
||||
void updateCollisionGroups();
|
||||
|
||||
signals:
|
||||
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
|
||||
|
@ -164,9 +161,7 @@ signals:
|
|||
protected:
|
||||
SkeletonModel _skeletonModel;
|
||||
float _bodyYawDelta;
|
||||
AvatarMode _mode;
|
||||
glm::vec3 _velocity;
|
||||
glm::vec3 _thrust;
|
||||
float _leanScale;
|
||||
float _scale;
|
||||
glm::vec3 _worldUpDirection;
|
||||
|
@ -175,7 +170,7 @@ protected:
|
|||
float _stringLength;
|
||||
bool _moving; ///< set when position is changing
|
||||
|
||||
uint32_t _collisionFlags;
|
||||
quint32 _collisionGroups;
|
||||
|
||||
// protected methods...
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
@ -54,16 +55,13 @@ MyAvatar::MyAvatar() :
|
|||
_shouldJump(false),
|
||||
_gravity(0.0f, -1.0f, 0.0f),
|
||||
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
|
||||
_elapsedTimeMoving(0.0f),
|
||||
_elapsedTimeStopped(0.0f),
|
||||
_elapsedTimeSinceCollision(0.0f),
|
||||
_lastCollisionPosition(0, 0, 0),
|
||||
_speedBrakes(false),
|
||||
_thrust(0.0f),
|
||||
_isThrustOn(false),
|
||||
_thrustMultiplier(1.0f),
|
||||
_moveTarget(0,0,0),
|
||||
_motionBehaviors(0),
|
||||
_lastBodyPenetration(0.0f),
|
||||
_moveTargetStepCounter(0),
|
||||
_lookAtTargetAvatar(),
|
||||
_shouldRender(true),
|
||||
_billboardValid(false),
|
||||
|
@ -97,11 +95,6 @@ void MyAvatar::reset() {
|
|||
setOrientation(glm::quat(glm::vec3(0.0f)));
|
||||
}
|
||||
|
||||
void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) {
|
||||
_moveTarget = moveTarget;
|
||||
_moveTargetStepCounter = 0;
|
||||
}
|
||||
|
||||
void MyAvatar::update(float deltaTime) {
|
||||
Head* head = getHead();
|
||||
head->relaxLean(deltaTime);
|
||||
|
@ -132,7 +125,7 @@ void MyAvatar::update(float deltaTime) {
|
|||
head->setAudioLoudness(audio->getLastInputLoudness());
|
||||
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) {
|
||||
if (_motionBehaviors & AVATAR_MOTION_OBEY_GRAVITY) {
|
||||
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
|
||||
} else {
|
||||
setGravity(glm::vec3(0.0f, 0.0f, 0.0f));
|
||||
|
@ -145,17 +138,6 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
|
||||
glm::quat orientation = getOrientation();
|
||||
|
||||
// Update movement timers
|
||||
_elapsedTimeSinceCollision += deltaTime;
|
||||
const float VELOCITY_MOVEMENT_TIMER_THRESHOLD = 0.2f;
|
||||
if (glm::length(_velocity) < VELOCITY_MOVEMENT_TIMER_THRESHOLD) {
|
||||
_elapsedTimeMoving = 0.0f;
|
||||
_elapsedTimeStopped += deltaTime;
|
||||
} else {
|
||||
_elapsedTimeStopped = 0.0f;
|
||||
_elapsedTimeMoving += deltaTime;
|
||||
}
|
||||
|
||||
if (_scale != _targetScale) {
|
||||
float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
|
||||
setScale(scale);
|
||||
|
@ -269,34 +251,11 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
// update the euler angles
|
||||
setOrientation(orientation);
|
||||
|
||||
const float WALKING_SPEED_THRESHOLD = 0.2f;
|
||||
// use speed and angular velocity to determine walking vs. standing
|
||||
float speed = glm::length(_velocity);
|
||||
if (speed + fabs(_bodyYawDelta) > WALKING_SPEED_THRESHOLD) {
|
||||
_mode = AVATAR_MODE_WALKING;
|
||||
} else {
|
||||
_mode = AVATAR_MODE_INTERACTING;
|
||||
}
|
||||
|
||||
// update moving flag based on speed
|
||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||
float speed = glm::length(_velocity);
|
||||
_moving = speed > MOVING_SPEED_THRESHOLD;
|
||||
|
||||
// If a move target is set, update position explicitly
|
||||
const float MOVE_FINISHED_TOLERANCE = 0.1f;
|
||||
const float MOVE_SPEED_FACTOR = 2.0f;
|
||||
const int MOVE_TARGET_MAX_STEPS = 250;
|
||||
if ((glm::length(_moveTarget) > EPSILON) && (_moveTargetStepCounter < MOVE_TARGET_MAX_STEPS)) {
|
||||
if (glm::length(_position - _moveTarget) > MOVE_FINISHED_TOLERANCE) {
|
||||
_position += (_moveTarget - _position) * (deltaTime * MOVE_SPEED_FACTOR);
|
||||
_moveTargetStepCounter++;
|
||||
} else {
|
||||
// Move completed
|
||||
_moveTarget = glm::vec3(0,0,0);
|
||||
_moveTargetStepCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
updateChatCircle(deltaTime);
|
||||
|
||||
_position += _velocity * deltaTime;
|
||||
|
@ -324,10 +283,10 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
head->simulate(deltaTime, true);
|
||||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0.0f);
|
||||
_thrust *= glm::vec3(0.0f);
|
||||
|
||||
// now that we're done stepping the avatar forward in time, compute new collisions
|
||||
if (_collisionFlags != 0) {
|
||||
if (_collisionGroups != 0) {
|
||||
Camera* myCamera = Application::getInstance()->getCamera();
|
||||
|
||||
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
|
||||
|
@ -335,15 +294,17 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f));
|
||||
radius *= COLLISION_RADIUS_SCALAR;
|
||||
}
|
||||
|
||||
if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
|
||||
updateCollisionWithEnvironment(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_VOXELS) {
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
if (_collisionGroups) {
|
||||
updateShapePositions();
|
||||
if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) {
|
||||
updateCollisionWithEnvironment(deltaTime, radius);
|
||||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
|
||||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
}
|
||||
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -449,8 +410,6 @@ void MyAvatar::renderDebugBodyPoints() {
|
|||
glTranslatef(position.x, position.y, position.z);
|
||||
glutSolidSphere(0.15, 10, 10);
|
||||
glPopMatrix();
|
||||
|
||||
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -460,6 +419,9 @@ void MyAvatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
|||
return; // exit early
|
||||
}
|
||||
Avatar::render(cameraPosition, renderMode);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints)) {
|
||||
_skeletonModel.renderIKConstraints();
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::renderHeadMouse() const {
|
||||
|
@ -795,19 +757,21 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
|
|||
}
|
||||
}
|
||||
|
||||
static CollisionList myCollisions(64);
|
||||
|
||||
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
||||
const float VOXEL_ELASTICITY = 0.4f;
|
||||
const float VOXEL_DAMPING = 0.0f;
|
||||
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||
glm::vec3 penetration;
|
||||
float pelvisFloatingHeight = getPelvisFloatingHeight();
|
||||
if (Application::getInstance()->getVoxelTree()->findCapsulePenetration(
|
||||
_position - glm::vec3(0.0f, pelvisFloatingHeight - radius, 0.0f),
|
||||
_position + glm::vec3(0.0f, getSkeletonHeight() - pelvisFloatingHeight + radius, 0.0f), radius, penetration)) {
|
||||
_lastCollisionPosition = _position;
|
||||
updateCollisionSound(penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
|
||||
applyHardCollision(penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
}
|
||||
myCollisions.clear();
|
||||
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
|
||||
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions)) {
|
||||
const float VOXEL_ELASTICITY = 0.4f;
|
||||
const float VOXEL_DAMPING = 0.0f;
|
||||
for (int i = 0; i < myCollisions.size(); ++i) {
|
||||
CollisionInfo* collision = myCollisions[i];
|
||||
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
||||
}
|
||||
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
||||
updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) {
|
||||
|
@ -823,7 +787,6 @@ void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity
|
|||
// cancel out the velocity component in the direction of penetration
|
||||
float penetrationLength = glm::length(penetration);
|
||||
if (penetrationLength > EPSILON) {
|
||||
_elapsedTimeSinceCollision = 0.0f;
|
||||
glm::vec3 direction = penetration / penetrationLength;
|
||||
_velocity -= glm::dot(_velocity, direction) * direction * (1.0f + elasticity);
|
||||
_velocity *= glm::clamp(1.0f - damping, 0.0f, 1.0f);
|
||||
|
@ -860,7 +823,7 @@ void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTim
|
|||
std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.0f),
|
||||
frequency * (1.0f + velocityTangentToCollision / velocityTowardCollision),
|
||||
std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.0f),
|
||||
1.0f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true);
|
||||
1.0f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -912,7 +875,6 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
// no need to compute a bunch of stuff if we have one or fewer avatars
|
||||
return;
|
||||
}
|
||||
updateShapePositions();
|
||||
float myBoundingRadius = getBoundingRadius();
|
||||
|
||||
const float BODY_COLLISION_RESOLUTION_FACTOR = glm::max(1.0f, deltaTime / BODY_COLLISION_RESOLUTION_TIMESCALE);
|
||||
|
@ -1178,8 +1140,31 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
|
|||
coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
||||
setPosition(newPosition);
|
||||
emit transformChanged();
|
||||
} else {
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MyAvatar::updateMotionBehaviors() {
|
||||
_motionBehaviors = 0;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::ObeyGravity)) {
|
||||
_motionBehaviors |= AVATAR_MOTION_OBEY_GRAVITY;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setCollisionGroups(quint32 collisionGroups) {
|
||||
Avatar::setCollisionGroups(collisionGroups & VALID_COLLISION_GROUPS);
|
||||
Menu* menu = Menu::getInstance();
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithEnvironment, (bool)(_collisionGroups & COLLISION_GROUP_ENVIRONMENT));
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithAvatars, (bool)(_collisionGroups & COLLISION_GROUP_AVATARS));
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithVoxels, (bool)(_collisionGroups & COLLISION_GROUP_VOXELS));
|
||||
menu->setIsOptionChecked(MenuOption::CollideWithParticles, (bool)(_collisionGroups & COLLISION_GROUP_PARTICLES));
|
||||
}
|
||||
|
||||
void MyAvatar::setMotionBehaviors(quint32 flags) {
|
||||
_motionBehaviors = flags;
|
||||
Menu* menu = Menu::getInstance();
|
||||
menu->setIsOptionChecked(MenuOption::ObeyGravity, (bool)(_motionBehaviors & AVATAR_MOTION_OBEY_GRAVITY));
|
||||
}
|
||||
|
||||
void MyAvatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) {
|
||||
|
|
|
@ -25,9 +25,12 @@ enum AvatarHandState
|
|||
NUM_HAND_STATES
|
||||
};
|
||||
|
||||
const quint32 AVATAR_MOTION_OBEY_GRAVITY = 1U << 0;
|
||||
|
||||
class MyAvatar : public Avatar {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
|
||||
Q_PROPERTY(quint32 motionBehaviors READ getMotionBehaviors WRITE setMotionBehaviors)
|
||||
|
||||
public:
|
||||
MyAvatar();
|
||||
|
@ -54,10 +57,7 @@ public:
|
|||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
|
||||
|
||||
// getters
|
||||
AvatarMode getMode() const { return _mode; }
|
||||
float getLeanScale() const { return _leanScale; }
|
||||
float getElapsedTimeStopped() const { return _elapsedTimeStopped; }
|
||||
float getElapsedTimeMoving() const { return _elapsedTimeMoving; }
|
||||
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
|
||||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
glm::vec3 getGravity() const { return _gravity; }
|
||||
|
@ -91,6 +91,10 @@ public:
|
|||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
virtual void setCollisionGroups(quint32 collisionGroups);
|
||||
void setMotionBehaviors(quint32 flags);
|
||||
quint32 getMotionBehaviors() const { return _motionBehaviors; }
|
||||
|
||||
void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration);
|
||||
|
||||
public slots:
|
||||
|
@ -107,6 +111,8 @@ public slots:
|
|||
glm::vec3 getThrust() { return _thrust; };
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
|
||||
void updateMotionBehaviors();
|
||||
|
||||
signals:
|
||||
void transformChanged();
|
||||
|
||||
|
@ -118,16 +124,17 @@ private:
|
|||
float _driveKeys[MAX_DRIVE_KEYS];
|
||||
glm::vec3 _gravity;
|
||||
float _distanceToNearestAvatar; // How close is the nearest avatar?
|
||||
float _elapsedTimeMoving; // Timers to drive camera transitions when moving
|
||||
float _elapsedTimeStopped;
|
||||
float _elapsedTimeSinceCollision;
|
||||
|
||||
// motion stuff
|
||||
glm::vec3 _lastCollisionPosition;
|
||||
bool _speedBrakes;
|
||||
glm::vec3 _thrust; // final acceleration for the current frame
|
||||
bool _isThrustOn;
|
||||
float _thrustMultiplier;
|
||||
glm::vec3 _moveTarget;
|
||||
|
||||
quint32 _motionBehaviors;
|
||||
|
||||
glm::vec3 _lastBodyPenetration;
|
||||
int _moveTargetStepCounter;
|
||||
QWeakPointer<AvatarData> _lookAtTargetAvatar;
|
||||
glm::vec3 _targetAvatarPosition;
|
||||
bool _shouldRender;
|
||||
|
|
|
@ -103,6 +103,11 @@ void SkeletonModel::getBodyShapes(QVector<const Shape*>& shapes) const {
|
|||
shapes.push_back(&_boundingShape);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderIKConstraints() {
|
||||
renderJointConstraints(getRightHandJointIndex());
|
||||
renderJointConstraints(getLeftHandJointIndex());
|
||||
}
|
||||
|
||||
class IndexValue {
|
||||
public:
|
||||
int index;
|
||||
|
@ -133,7 +138,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
|||
|
||||
// align hand with forearm
|
||||
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
|
||||
applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), false);
|
||||
applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector));
|
||||
}
|
||||
|
||||
void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJointIndices,
|
||||
|
@ -148,12 +153,15 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
|
|||
return;
|
||||
}
|
||||
|
||||
// rotate forearm to align with palm direction
|
||||
// rotate palm to align with palm direction
|
||||
glm::quat palmRotation;
|
||||
getJointRotation(parentJointIndex, palmRotation, true);
|
||||
applyRotationDelta(parentJointIndex, rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()), false);
|
||||
getJointRotation(parentJointIndex, palmRotation, true);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||
getJointRotation(parentJointIndex, palmRotation, true);
|
||||
} else {
|
||||
getJointRotation(jointIndex, palmRotation, true);
|
||||
}
|
||||
palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()) * palmRotation;
|
||||
|
||||
// sort the finger indices by raw x, get the average direction
|
||||
QVector<IndexValue> fingerIndices;
|
||||
glm::vec3 direction;
|
||||
|
@ -173,17 +181,20 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
|
|||
float directionLength = glm::length(direction);
|
||||
const unsigned int MIN_ROTATION_FINGERS = 3;
|
||||
if (directionLength > EPSILON && palm.getNumFingers() >= MIN_ROTATION_FINGERS) {
|
||||
applyRotationDelta(parentJointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction), false);
|
||||
getJointRotation(parentJointIndex, palmRotation, true);
|
||||
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction) * palmRotation;
|
||||
}
|
||||
|
||||
// let wrist inherit forearm rotation
|
||||
_jointStates[jointIndex].rotation = glm::quat();
|
||||
|
||||
// set elbow position from wrist position
|
||||
glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f);
|
||||
setJointPosition(parentJointIndex, palm.getPosition() + forearmVector *
|
||||
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale));
|
||||
// set hand position, rotation
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||
glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f);
|
||||
setJointPosition(parentJointIndex, palm.getPosition() + forearmVector *
|
||||
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale));
|
||||
setJointRotation(parentJointIndex, palmRotation, true);
|
||||
_jointStates[jointIndex].rotation = glm::quat();
|
||||
|
||||
} else {
|
||||
setJointPosition(jointIndex, palm.getPosition(), palmRotation, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::updateJointState(int index) {
|
||||
|
@ -210,3 +221,59 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
|
|||
glm::normalize(inverse * axes[0])) * joint.rotation;
|
||||
}
|
||||
|
||||
void SkeletonModel::renderJointConstraints(int jointIndex) {
|
||||
if (jointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const float BASE_DIRECTION_SIZE = 300.0f;
|
||||
float directionSize = BASE_DIRECTION_SIZE * extractUniformScale(_scale);
|
||||
glLineWidth(3.0f);
|
||||
do {
|
||||
const FBXJoint& joint = geometry.joints.at(jointIndex);
|
||||
const JointState& jointState = _jointStates.at(jointIndex);
|
||||
glm::vec3 position = extractTranslation(jointState.transform) + _translation;
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _jointStates.at(joint.parentIndex).combinedRotation;
|
||||
glm::vec3 rotationAxis = glm::axis(parentRotation);
|
||||
glRotatef(glm::degrees(glm::angle(parentRotation)), rotationAxis.x, rotationAxis.y, rotationAxis.z);
|
||||
float fanScale = directionSize * 0.75f;
|
||||
glScalef(fanScale, fanScale, fanScale);
|
||||
const int AXIS_COUNT = 3;
|
||||
for (int i = 0; i < AXIS_COUNT; i++) {
|
||||
if (joint.rotationMin[i] <= -PI + EPSILON && joint.rotationMax[i] >= PI - EPSILON) {
|
||||
continue; // unconstrained
|
||||
}
|
||||
glm::vec3 axis;
|
||||
axis[i] = 1.0f;
|
||||
|
||||
glm::vec3 otherAxis;
|
||||
if (i == 0) {
|
||||
otherAxis.y = 1.0f;
|
||||
} else {
|
||||
otherAxis.x = 1.0f;
|
||||
}
|
||||
glColor4f(otherAxis.r, otherAxis.g, otherAxis.b, 0.75f);
|
||||
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
const int FAN_SEGMENTS = 16;
|
||||
for (int j = 0; j < FAN_SEGMENTS; j++) {
|
||||
glm::vec3 rotated = glm::angleAxis(glm::mix(joint.rotationMin[i], joint.rotationMax[i],
|
||||
(float)j / (FAN_SEGMENTS - 1)), axis) * otherAxis;
|
||||
glVertex3f(rotated.x, rotated.y, rotated.z);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
renderOrientationDirections(position, jointState.combinedRotation, directionSize);
|
||||
jointIndex = joint.parentIndex;
|
||||
|
||||
} while (jointIndex != -1 && geometry.joints.at(jointIndex).isFree);
|
||||
|
||||
glLineWidth(1.0f);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
/// \param shapes[out] list of shapes for body collisions
|
||||
void getBodyShapes(QVector<const Shape*>& shapes) const;
|
||||
|
||||
void renderIKConstraints();
|
||||
|
||||
protected:
|
||||
|
||||
void applyHandPosition(int jointIndex, const glm::vec3& position);
|
||||
|
@ -46,6 +48,8 @@ protected:
|
|||
virtual void maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
|
||||
private:
|
||||
|
||||
void renderJointConstraints(int jointIndex);
|
||||
|
||||
Avatar* _owningAvatar;
|
||||
};
|
||||
|
|
|
@ -11,10 +11,6 @@
|
|||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
|
||||
#include <QOpenGLFramebufferObject>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
|
|
@ -15,11 +15,6 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include "TV3DManager.h"
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "Application.h"
|
||||
#include "LocationManager.h"
|
||||
|
||||
|
@ -118,6 +120,8 @@ void LocationManager::checkForMultipleDestinations() {
|
|||
Application::getInstance()->getAvatar()->goToLocationFromResponse(_placeData);
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
#include <SharedUtil.h>
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
timeval startup_time;
|
||||
gettimeofday(&startup_time, NULL);
|
||||
QElapsedTimer startupTime;
|
||||
startupTime.start();
|
||||
|
||||
// Debug option to demonstrate that the client's local time does not
|
||||
// need to be in sync with any other network node. This forces clock
|
||||
|
@ -33,7 +33,7 @@ int main(int argc, const char * argv[]) {
|
|||
int exitCode;
|
||||
{
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
Application app(argc, const_cast<char**>(argv), startup_time);
|
||||
Application app(argc, const_cast<char**>(argv), startupTime);
|
||||
|
||||
QTranslator translator;
|
||||
translator.load("interface_en");
|
||||
|
|
|
@ -882,12 +882,12 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int lastFreeIndex,
|
||||
bool allIntermediatesFree, const glm::vec3& alignment) {
|
||||
bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation,
|
||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
glm::vec3 relativePosition = position - _translation;
|
||||
glm::vec3 relativePosition = translation - _translation;
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
|
||||
if (freeLineage.isEmpty()) {
|
||||
|
@ -896,13 +896,21 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int last
|
|||
if (lastFreeIndex == -1) {
|
||||
lastFreeIndex = freeLineage.last();
|
||||
}
|
||||
|
||||
|
||||
// this is a cyclic coordinate descent algorithm: see
|
||||
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
|
||||
const int ITERATION_COUNT = 1;
|
||||
glm::vec3 worldAlignment = _rotation * alignment;
|
||||
for (int i = 0; i < ITERATION_COUNT; i++) {
|
||||
// first, we go from the joint upwards, rotating the end as close as possible to the target
|
||||
// first, try to rotate the end effector as close as possible to the target rotation, if any
|
||||
glm::quat endRotation;
|
||||
if (useRotation) {
|
||||
getJointRotation(jointIndex, endRotation, true);
|
||||
applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation));
|
||||
getJointRotation(jointIndex, endRotation, true);
|
||||
}
|
||||
|
||||
// then, we go from the joint upwards, rotating the end as close as possible to the target
|
||||
glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].transform);
|
||||
for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) {
|
||||
int index = freeLineage.at(j);
|
||||
|
@ -914,8 +922,17 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int last
|
|||
glm::vec3 jointPosition = extractTranslation(state.transform);
|
||||
glm::vec3 jointVector = endPosition - jointPosition;
|
||||
glm::quat oldCombinedRotation = state.combinedRotation;
|
||||
applyRotationDelta(index, rotationBetween(jointVector, relativePosition - jointPosition));
|
||||
endPosition = state.combinedRotation * glm::inverse(oldCombinedRotation) * jointVector + jointPosition;
|
||||
glm::quat combinedDelta;
|
||||
float combinedWeight;
|
||||
if (useRotation) {
|
||||
combinedDelta = safeMix(rotation * glm::inverse(endRotation),
|
||||
rotationBetween(jointVector, relativePosition - jointPosition), 0.5f);
|
||||
combinedWeight = 2.0f;
|
||||
|
||||
} else {
|
||||
combinedDelta = rotationBetween(jointVector, relativePosition - jointPosition);
|
||||
combinedWeight = 1.0f;
|
||||
}
|
||||
if (alignment != glm::vec3() && j > 1) {
|
||||
jointVector = endPosition - jointPosition;
|
||||
glm::vec3 positionSum;
|
||||
|
@ -929,9 +946,16 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int last
|
|||
glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector));
|
||||
const float LENGTH_EPSILON = 0.001f;
|
||||
if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) {
|
||||
applyRotationDelta(index, rotationBetween(projectedCenterOfMass, projectedAlignment));
|
||||
combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment),
|
||||
1.0f / (combinedWeight + 1.0f));
|
||||
}
|
||||
}
|
||||
applyRotationDelta(index, combinedDelta);
|
||||
glm::quat actualDelta = state.combinedRotation * glm::inverse(oldCombinedRotation);
|
||||
endPosition = actualDelta * jointVector + jointPosition;
|
||||
if (useRotation) {
|
||||
endRotation = actualDelta * endRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1012,8 +1036,9 @@ void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool cons
|
|||
state.combinedRotation = delta * state.combinedRotation;
|
||||
return;
|
||||
}
|
||||
glm::quat newRotation = glm::quat(glm::clamp(safeEulerAngles(state.rotation *
|
||||
glm::inverse(state.combinedRotation) * delta * state.combinedRotation), joint.rotationMin, joint.rotationMax));
|
||||
glm::quat targetRotation = delta * state.combinedRotation;
|
||||
glm::vec3 eulers = safeEulerAngles(state.rotation * glm::inverse(state.combinedRotation) * targetRotation);
|
||||
glm::quat newRotation = glm::quat(glm::clamp(eulers, joint.rotationMin, joint.rotationMax));
|
||||
state.combinedRotation = state.combinedRotation * glm::inverse(state.rotation) * newRotation;
|
||||
state.rotation = newRotation;
|
||||
}
|
||||
|
@ -1141,7 +1166,7 @@ void Model::applyCollision(CollisionInfo& collision) {
|
|||
getJointPosition(jointIndex, end);
|
||||
glm::vec3 newEnd = start + glm::angleAxis(angle, axis) * (end - start);
|
||||
// try to move it
|
||||
setJointPosition(jointIndex, newEnd, -1, true);
|
||||
setJointPosition(jointIndex, newEnd, glm::quat(), false, -1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,6 +193,8 @@ public:
|
|||
/// Sets blended vertices computed in a separate thread.
|
||||
void setBlendedVertices(const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
|
||||
|
||||
const CapsuleShape& getBoundingShape() const { return _boundingShape; }
|
||||
|
||||
protected:
|
||||
|
||||
QSharedPointer<NetworkGeometry> _geometry;
|
||||
|
@ -240,8 +242,9 @@ protected:
|
|||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const;
|
||||
|
||||
bool setJointPosition(int jointIndex, const glm::vec3& position, int lastFreeIndex = -1,
|
||||
bool allIntermediatesFree = false, const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f));
|
||||
bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(),
|
||||
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
|
||||
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f));
|
||||
bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false);
|
||||
|
||||
void setJointTranslation(int jointIndex, const glm::vec3& translation);
|
||||
|
|
|
@ -9,24 +9,23 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "starfield/Controller.h"
|
||||
|
||||
using namespace starfield;
|
||||
|
||||
bool Controller::computeStars(unsigned numStars, unsigned seed) {
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
QElapsedTimer startTime;
|
||||
startTime.start();
|
||||
|
||||
Generator::computeStarPositions(_inputSequence, numStars, seed);
|
||||
|
||||
this->retile(numStars, _tileResolution);
|
||||
|
||||
qDebug() << "Total time to retile and generate stars: "
|
||||
<< ((usecTimestampNow() - usecTimestamp(&startTime)) / 1000) << "msec";
|
||||
double NSEC_TO_MSEC = 1.0 / 1000000.0;
|
||||
double timeDiff = (double)startTime.nsecsElapsed() * NSEC_TO_MSEC;
|
||||
qDebug() << "Total time to retile and generate stars: " << timeDiff << "msec";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,9 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "starfield/Generator.h"
|
||||
|
||||
|
@ -24,8 +22,8 @@ void Generator::computeStarPositions(InputVertices& destination, unsigned limit,
|
|||
InputVertices* vertices = & destination;
|
||||
//_limit = limit;
|
||||
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
QElapsedTimer startTime;
|
||||
startTime.start();
|
||||
|
||||
srand(seed);
|
||||
|
||||
|
@ -70,7 +68,8 @@ void Generator::computeStarPositions(InputVertices& destination, unsigned limit,
|
|||
vertices->push_back(InputVertex(azimuth, altitude, computeStarColor(STAR_COLORIZATION)));
|
||||
}
|
||||
|
||||
qDebug() << "Total time to generate stars: " << ((usecTimestampNow() - usecTimestamp(&startTime)) / 1000) << " msec";
|
||||
double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms
|
||||
qDebug() << "Total time to generate stars: " << timeDiff << " msec";
|
||||
}
|
||||
|
||||
// computeStarColor
|
||||
|
|
|
@ -62,26 +62,21 @@ BandwidthMeter::~BandwidthMeter() {
|
|||
free(_channels);
|
||||
}
|
||||
|
||||
BandwidthMeter::Stream::Stream(float msToAverage) :
|
||||
_value(0.0f),
|
||||
_msToAverage(msToAverage) {
|
||||
|
||||
gettimeofday(& _prevTime, NULL);
|
||||
BandwidthMeter::Stream::Stream(float msToAverage) : _value(0.0f), _msToAverage(msToAverage) {
|
||||
_prevTime.start();
|
||||
}
|
||||
|
||||
void BandwidthMeter::Stream::updateValue(double amount) {
|
||||
|
||||
// Determine elapsed time
|
||||
timeval now;
|
||||
gettimeofday(& now, NULL);
|
||||
double dt = diffclock(& _prevTime, & now);
|
||||
double dt = (double)_prevTime.nsecsElapsed() / 1000000.0; // ns to ms
|
||||
|
||||
// Ignore this value when timer imprecision yields dt = 0
|
||||
if (dt == 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(& _prevTime, & now, sizeof(timeval));
|
||||
_prevTime.start();
|
||||
|
||||
// Compute approximate average
|
||||
_value = glm::mix(_value, amount / dt,
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
#ifndef hifi_BandwidthMeter_h
|
||||
#define hifi_BandwidthMeter_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
@ -59,7 +57,7 @@ public:
|
|||
private:
|
||||
double _value; // Current value.
|
||||
double _msToAverage; // Milliseconds to average.
|
||||
timeval _prevTime; // Time of last feed.
|
||||
QElapsedTimer _prevTime; // Time of last feed.
|
||||
};
|
||||
|
||||
// Data model accessors
|
||||
|
|
19
interface/src/ui/ChatInputArea.cpp
Normal file
19
interface/src/ui/ChatInputArea.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// ChatInputArea.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Ryan Huffman on 4/24/14.
|
||||
// Copyright 2014 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
|
||||
//
|
||||
|
||||
#include "ChatInputArea.h"
|
||||
|
||||
ChatInputArea::ChatInputArea(QWidget* parent) : QTextEdit(parent) {
|
||||
};
|
||||
|
||||
void ChatInputArea::insertFromMimeData(const QMimeData* source) {
|
||||
insertPlainText(source->text());
|
||||
};
|
|
@ -13,16 +13,15 @@
|
|||
#define hifi_ChatInputArea_h
|
||||
|
||||
#include <QTextBrowser>
|
||||
#include <QMimeData>
|
||||
|
||||
class ChatInputArea : public QTextEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChatInputArea(QWidget* parent) : QTextEdit(parent) { };
|
||||
ChatInputArea(QWidget* parent);
|
||||
|
||||
protected:
|
||||
void insertFromMimeData(const QMimeData* source) {
|
||||
insertPlainText(source->text());
|
||||
};
|
||||
void insertFromMimeData(const QMimeData* source);
|
||||
};
|
||||
|
||||
#endif // hifi_ChatInputArea_h
|
||||
|
|
|
@ -32,11 +32,6 @@ signals:
|
|||
protected:
|
||||
virtual void wheelEvent(QWheelEvent* event);
|
||||
bool _useFixedHeight;
|
||||
|
||||
void insertFromMimeData(const QMimeData* source) {
|
||||
insertPlainText(source->text());
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_ChatMessageArea_h
|
||||
|
|
|
@ -184,6 +184,7 @@ void PreferencesDialog::savePreferences() {
|
|||
Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value());
|
||||
|
||||
Menu::getInstance()->setAudioJitterBufferSamples(ui.audioJitterSpin->value());
|
||||
Application::getInstance()->getAudio()->setJitterBufferSamples(ui.audioJitterSpin->value());
|
||||
|
||||
Application::getInstance()->resizeGL(Application::getInstance()->getGLWidget()->width(),
|
||||
Application::getInstance()->getGLWidget()->height());
|
||||
|
|
|
@ -39,7 +39,8 @@ ScriptEditorWidget::ScriptEditorWidget() :
|
|||
setTitleBarWidget(new QWidget());
|
||||
QFontMetrics fm(_scriptEditorWidgetUI->scriptEdit->font());
|
||||
_scriptEditorWidgetUI->scriptEdit->setTabStopWidth(fm.width('0') * 4);
|
||||
ScriptHighlighting* highlighting = new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document());
|
||||
// We create a new ScriptHighligting QObject and provide it with a parent so this is NOT a memory leak.
|
||||
new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document());
|
||||
QTimer::singleShot(0, _scriptEditorWidgetUI->scriptEdit, SLOT(setFocus()));
|
||||
}
|
||||
|
||||
|
|
|
@ -186,9 +186,9 @@ QPushButton:pressed {
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ChatInputArea" name="messagePlainTextEdit">
|
||||
<widget class="QFrame" name="chatFrame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
|
@ -196,33 +196,77 @@ QPushButton:pressed {
|
|||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
<height>78</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;</string>
|
||||
<string notr="true">#chatFrame {
|
||||
border-color: palette(dark); border-style: solid; border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="acceptRichText">
|
||||
<bool>false</bool>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ChatInputArea" name="messagePlainTextEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="acceptRichText">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -237,7 +281,6 @@ QPushButton:pressed {
|
|||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>messagePlainTextEdit</tabstop>
|
||||
<tabstop>messagesScrollArea</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
|
|
@ -71,8 +71,8 @@ void AudioInjector::injectAudio() {
|
|||
quint8 volume = MAX_INJECTOR_VOLUME * _options.getVolume();
|
||||
packetStream << volume;
|
||||
|
||||
timeval startTime = {};
|
||||
gettimeofday(&startTime, NULL);
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
int nextFrame = 0;
|
||||
|
||||
int currentSendPosition = 0;
|
||||
|
@ -104,7 +104,7 @@ void AudioInjector::injectAudio() {
|
|||
if (currentSendPosition != bytesToCopy && currentSendPosition < soundByteArray.size()) {
|
||||
// not the first packet and not done
|
||||
// sleep for the appropriate time
|
||||
int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();
|
||||
int usecToSleep = (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - timer.nsecsElapsed() / 1000;
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
|
|
|
@ -282,10 +282,13 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
|
|||
QUrl grantURL = _authURL;
|
||||
grantURL.setPath("/oauth/token");
|
||||
|
||||
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
|
||||
|
||||
QByteArray postData;
|
||||
postData.append("grant_type=password&");
|
||||
postData.append("username=" + login + "&");
|
||||
postData.append("password=" + password);
|
||||
postData.append("password=" + password + "&");
|
||||
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
|
||||
|
||||
request.setUrl(grantURL);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
|
|
@ -12,12 +12,6 @@
|
|||
#ifndef hifi_Assignment_h
|
||||
#define hifi_Assignment_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Systime.h"
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "NodeList.h"
|
||||
|
|
|
@ -20,18 +20,20 @@
|
|||
|
||||
#include <QDebug>
|
||||
|
||||
#include "CoverageMap.h"
|
||||
#include <GeometryUtil.h>
|
||||
#include "OctalCode.h"
|
||||
#include <OctalCode.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <Shape.h>
|
||||
#include <ShapeCollider.h>
|
||||
|
||||
//#include "Tags.h"
|
||||
|
||||
#include "ViewFrustum.h"
|
||||
#include "CoverageMap.h"
|
||||
#include "OctreeConstants.h"
|
||||
#include "OctreeElementBag.h"
|
||||
#include "Octree.h"
|
||||
#include "ViewFrustum.h"
|
||||
|
||||
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) {
|
||||
return voxelSizeScale / powf(2, renderLevel);
|
||||
|
@ -540,6 +542,19 @@ OctreeElement* Octree::getOctreeElementAt(float x, float y, float z, float s) co
|
|||
return node;
|
||||
}
|
||||
|
||||
OctreeElement* Octree::getOctreeEnclosingElementAt(float x, float y, float z, float s) const {
|
||||
unsigned char* octalCode = pointToOctalCode(x,y,z,s);
|
||||
OctreeElement* node = nodeForOctalCode(_rootNode, octalCode, NULL);
|
||||
|
||||
delete[] octalCode; // cleanup memory
|
||||
#ifdef HAS_AUDIT_CHILDREN
|
||||
if (node) {
|
||||
node->auditChildren("Octree::getOctreeElementAt()");
|
||||
}
|
||||
#endif // def HAS_AUDIT_CHILDREN
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
OctreeElement* Octree::getOrCreateChildElementAt(float x, float y, float z, float s) {
|
||||
return getRoot()->getOrCreateChildElementAt(x, y, z, s);
|
||||
|
@ -676,6 +691,13 @@ public:
|
|||
bool found;
|
||||
};
|
||||
|
||||
class ShapeArgs {
|
||||
public:
|
||||
const Shape* shape;
|
||||
CollisionList& collisions;
|
||||
bool found;
|
||||
};
|
||||
|
||||
bool findCapsulePenetrationOp(OctreeElement* node, void* extraData) {
|
||||
CapsuleArgs* args = static_cast<CapsuleArgs*>(extraData);
|
||||
|
||||
|
@ -697,6 +719,27 @@ bool findCapsulePenetrationOp(OctreeElement* node, void* extraData) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool findShapeCollisionsOp(OctreeElement* node, void* extraData) {
|
||||
ShapeArgs* args = static_cast<ShapeArgs*>(extraData);
|
||||
|
||||
// coarse check against bounds
|
||||
AABox cube = node->getAABox();
|
||||
cube.scale(TREE_SCALE);
|
||||
if (!cube.expandedContains(args->shape->getPosition(), args->shape->getBoundingRadius())) {
|
||||
return false;
|
||||
}
|
||||
if (!node->isLeaf()) {
|
||||
return true; // recurse on children
|
||||
}
|
||||
if (node->hasContent()) {
|
||||
if (ShapeCollider::collideShapeWithAACube(args->shape, cube.calcCenter(), cube.getScale(), args->collisions)) {
|
||||
args->found = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
|
||||
glm::vec3& penetration, Octree::lockType lockType) {
|
||||
|
||||
|
@ -727,6 +770,29 @@ bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end
|
|||
return args.found;
|
||||
}
|
||||
|
||||
bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions, Octree::lockType lockType) {
|
||||
|
||||
ShapeArgs args = { shape, collisions, false };
|
||||
|
||||
bool gotLock = false;
|
||||
if (lockType == Octree::Lock) {
|
||||
lockForRead();
|
||||
gotLock = true;
|
||||
} else if (lockType == Octree::TryLock) {
|
||||
gotLock = tryLockForRead();
|
||||
if (!gotLock) {
|
||||
return args.found; // if we wanted to tryLock, and we couldn't then just bail...
|
||||
}
|
||||
}
|
||||
|
||||
recurseTreeWithOperation(findShapeCollisionsOp, &args);
|
||||
|
||||
if (gotLock) {
|
||||
unlock();
|
||||
}
|
||||
return args.found;
|
||||
}
|
||||
|
||||
class GetElementEnclosingArgs {
|
||||
public:
|
||||
OctreeElement* element;
|
||||
|
|
|
@ -21,6 +21,7 @@ class Octree;
|
|||
class OctreeElement;
|
||||
class OctreeElementBag;
|
||||
class OctreePacketData;
|
||||
class Shape;
|
||||
|
||||
|
||||
#include "JurisdictionMap.h"
|
||||
|
@ -30,6 +31,8 @@ class OctreePacketData;
|
|||
#include "OctreePacketData.h"
|
||||
#include "OctreeSceneStats.h"
|
||||
|
||||
#include <CollisionInfo.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
|
@ -207,7 +210,15 @@ public:
|
|||
void reaverageOctreeElements(OctreeElement* startNode = NULL);
|
||||
|
||||
void deleteOctreeElementAt(float x, float y, float z, float s);
|
||||
|
||||
/// Find the voxel at position x,y,z,s
|
||||
/// \return pointer to the OctreeElement or NULL if none at x,y,z,s.
|
||||
OctreeElement* getOctreeElementAt(float x, float y, float z, float s) const;
|
||||
|
||||
/// Find the voxel at position x,y,z,s
|
||||
/// \return pointer to the OctreeElement or to the smallest enclosing parent if none at x,y,z,s.
|
||||
OctreeElement* getOctreeEnclosingElementAt(float x, float y, float z, float s) const;
|
||||
|
||||
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
|
||||
|
||||
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL);
|
||||
|
@ -246,6 +257,8 @@ public:
|
|||
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
|
||||
glm::vec3& penetration, Octree::lockType lockType = Octree::TryLock);
|
||||
|
||||
bool findShapeCollisions(const Shape* shape, CollisionList& collisions, Octree::lockType = Octree::TryLock);
|
||||
|
||||
OctreeElement* getElementEnclosingPoint(const glm::vec3& point, Octree::lockType lockType = Octree::TryLock);
|
||||
|
||||
// Note: this assumes the fileFormat is the HIO individual voxels code files
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
|
||||
#include "AABox.h"
|
||||
#include "OctalCode.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "OctreeConstants.h"
|
||||
#include "OctreeElement.h"
|
||||
#include "Octree.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
quint64 OctreeElement::_voxelMemoryUsage = 0;
|
||||
quint64 OctreeElement::_octcodeMemoryUsage = 0;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <QReadWriteLock>
|
||||
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "AABox.h"
|
||||
#include "ViewFrustum.h"
|
||||
#include "OctreeConstants.h"
|
||||
|
|
|
@ -820,11 +820,7 @@ const char* OctreeSceneStats::getItemValue(Item item) {
|
|||
|
||||
void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
||||
bool wasStatsPacket, int nodeClockSkewUsec) {
|
||||
_incomingPacket++;
|
||||
_incomingBytes += packet.size();
|
||||
if (!wasStatsPacket) {
|
||||
_incomingWastedBytes += (MAX_PACKET_SIZE - packet.size());
|
||||
}
|
||||
const bool wantExtraDebugging = false;
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||
const unsigned char* dataAt = reinterpret_cast<const unsigned char*>(packet.data()) + numBytesPacketHeader;
|
||||
|
@ -842,12 +838,43 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
|||
|
||||
OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow();
|
||||
int flightTime = arrivedAt - sentAt + nodeClockSkewUsec;
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
qDebug() << "sentAt:" << sentAt << " usecs";
|
||||
qDebug() << "arrivedAt:" << arrivedAt << " usecs";
|
||||
qDebug() << "nodeClockSkewUsec:" << nodeClockSkewUsec << " usecs";
|
||||
qDebug() << "flightTime:" << flightTime << " usecs";
|
||||
}
|
||||
|
||||
// Guard against possible corrupted packets... with bad timestamps
|
||||
const int MAX_RESONABLE_FLIGHT_TIME = 200 * USECS_PER_SECOND; // 200 seconds is more than enough time for a packet to arrive
|
||||
const int MIN_RESONABLE_FLIGHT_TIME = 0;
|
||||
if (flightTime > MAX_RESONABLE_FLIGHT_TIME || flightTime < MIN_RESONABLE_FLIGHT_TIME) {
|
||||
qDebug() << "ignoring unreasonable packet... flightTime:" << flightTime;
|
||||
return; // ignore any packets that are unreasonable
|
||||
}
|
||||
|
||||
// Guard against possible corrupted packets... with bad sequence numbers
|
||||
const int MAX_RESONABLE_SEQUENCE_OFFSET = 2000;
|
||||
const int MIN_RESONABLE_SEQUENCE_OFFSET = -2000;
|
||||
int sequenceOffset = (sequence - _incomingLastSequence);
|
||||
if (sequenceOffset > MAX_RESONABLE_SEQUENCE_OFFSET || sequenceOffset < MIN_RESONABLE_SEQUENCE_OFFSET) {
|
||||
qDebug() << "ignoring unreasonable packet... sequence:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
|
||||
return; // ignore any packets that are unreasonable
|
||||
}
|
||||
|
||||
// track packets here...
|
||||
_incomingPacket++;
|
||||
_incomingBytes += packet.size();
|
||||
if (!wasStatsPacket) {
|
||||
_incomingWastedBytes += (MAX_PACKET_SIZE - packet.size());
|
||||
}
|
||||
|
||||
const int USECS_PER_MSEC = 1000;
|
||||
float flightTimeMsecs = flightTime / USECS_PER_MSEC;
|
||||
_incomingFlightTimeAverage.updateAverage(flightTimeMsecs);
|
||||
|
||||
// track out of order and possibly lost packets...
|
||||
const bool wantExtraDebugging = false;
|
||||
if (sequence == _incomingLastSequence) {
|
||||
if (wantExtraDebugging) {
|
||||
qDebug() << "last packet duplicate got:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
#include "AABox.h"
|
||||
#include "Plane.h"
|
||||
|
||||
#include "OctreeConstants.h"
|
||||
#include "OctreeProjectedPolygon.h"
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <AudioRingBuffer.h>
|
||||
#include <AvatarData.h>
|
||||
#include <CollisionInfo.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <UUID.h>
|
||||
|
@ -230,8 +231,13 @@ void ScriptEngine::init() {
|
|||
|
||||
registerGlobalObject("Voxels", &_voxelsScriptingInterface);
|
||||
|
||||
QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE));
|
||||
_engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
|
||||
// constants
|
||||
QScriptValue globalObject = _engine.globalObject();
|
||||
globalObject.setProperty("TREE_SCALE", _engine.newVariant(QVariant(TREE_SCALE)));
|
||||
globalObject.setProperty("COLLISION_GROUP_ENVIRONMENT", _engine.newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT)));
|
||||
globalObject.setProperty("COLLISION_GROUP_AVATARS", _engine.newVariant(QVariant(COLLISION_GROUP_AVATARS)));
|
||||
globalObject.setProperty("COLLISION_GROUP_VOXELS", _engine.newVariant(QVariant(COLLISION_GROUP_VOXELS)));
|
||||
globalObject.setProperty("COLLISION_GROUP_PARTICLES", _engine.newVariant(QVariant(COLLISION_GROUP_PARTICLES)));
|
||||
|
||||
// let the VoxelPacketSender know how frequently we plan to call it
|
||||
_voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(SCRIPT_DATA_CALLBACK_USECS);
|
||||
|
@ -286,8 +292,8 @@ void ScriptEngine::run() {
|
|||
emit errorMessage("Uncaught exception at line" + QString::number(line) + ":" + result.toString());
|
||||
}
|
||||
|
||||
timeval startTime;
|
||||
gettimeofday(&startTime, NULL);
|
||||
QElapsedTimer startTime;
|
||||
startTime.start();
|
||||
|
||||
int thisFrame = 0;
|
||||
|
||||
|
@ -296,7 +302,7 @@ void ScriptEngine::run() {
|
|||
qint64 lastUpdate = usecTimestampNow();
|
||||
|
||||
while (!_isFinished) {
|
||||
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - usecTimestampNow();
|
||||
int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
|
@ -561,4 +567,4 @@ void ScriptEngine::include(const QString& includeFile) {
|
|||
qDebug() << "Uncaught exception at (" << includeFile << ") line" << line << ":" << result.toString();
|
||||
emit errorMessage("Uncaught exception at (" + includeFile + ") line" + QString::number(line) + ":" + result.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,3 +48,6 @@ void CollisionList::clear() {
|
|||
_size = 0;
|
||||
}
|
||||
|
||||
CollisionInfo* CollisionList::operator[](int index) {
|
||||
return (index > -1 && index < _size) ? &(_collisions[index]) : NULL;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0;
|
|||
const quint32 COLLISION_GROUP_AVATARS = 1U << 1;
|
||||
const quint32 COLLISION_GROUP_VOXELS = 1U << 2;
|
||||
const quint32 COLLISION_GROUP_PARTICLES = 1U << 3;
|
||||
const quint32 VALID_COLLISION_GROUPS = 0x0f;
|
||||
|
||||
// CollisionInfo contains details about the collision between two things: BodyA and BodyB.
|
||||
// The assumption is that the context that analyzes the collision knows about BodyA but
|
||||
|
@ -95,6 +96,8 @@ public:
|
|||
/// Clear valid collisions.
|
||||
void clear();
|
||||
|
||||
CollisionInfo* operator[](int index);
|
||||
|
||||
private:
|
||||
int _maxSize; // the container cannot get larger than this
|
||||
int _size; // the current number of valid collisions in the list
|
||||
|
|
|
@ -18,12 +18,6 @@
|
|||
#include <stdint.h>
|
||||
#include "SharedUtil.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Systime.h"
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
|
|
@ -92,6 +92,29 @@ bool collideShapesCoarse(const QVector<const Shape*>& shapesA, const QVector<con
|
|||
return false;
|
||||
}
|
||||
|
||||
bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) {
|
||||
int typeA = shapeA->getType();
|
||||
if (typeA == Shape::SPHERE_SHAPE) {
|
||||
return sphereAACube(static_cast<const SphereShape*>(shapeA), cubeCenter, cubeSide, collisions);
|
||||
} else if (typeA == Shape::CAPSULE_SHAPE) {
|
||||
return capsuleAACube(static_cast<const CapsuleShape*>(shapeA), cubeCenter, cubeSide, collisions);
|
||||
} else if (typeA == Shape::LIST_SHAPE) {
|
||||
const ListShape* listA = static_cast<const ListShape*>(shapeA);
|
||||
bool touching = false;
|
||||
for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) {
|
||||
const Shape* subShape = listA->getSubShape(i);
|
||||
int subType = subShape->getType();
|
||||
if (subType == Shape::SPHERE_SHAPE) {
|
||||
touching = sphereAACube(static_cast<const SphereShape*>(subShape), cubeCenter, cubeSide, collisions) || touching;
|
||||
} else if (subType == Shape::CAPSULE_SHAPE) {
|
||||
touching = capsuleAACube(static_cast<const CapsuleShape*>(subShape), cubeCenter, cubeSide, collisions) || touching;
|
||||
}
|
||||
}
|
||||
return touching;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions) {
|
||||
glm::vec3 BA = sphereB->getPosition() - sphereA->getPosition();
|
||||
float distanceSquared = glm::dot(BA, BA);
|
||||
|
@ -567,4 +590,103 @@ bool listList(const ListShape* listA, const ListShape* listB, CollisionList& col
|
|||
return touching;
|
||||
}
|
||||
|
||||
// helper function
|
||||
bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) {
|
||||
glm::vec3 BA = cubeCenter - sphereCenter;
|
||||
float distance = glm::length(BA);
|
||||
if (distance > EPSILON) {
|
||||
BA /= distance; // BA is now normalized
|
||||
// compute the nearest point on sphere
|
||||
glm::vec3 surfaceA = sphereCenter + sphereRadius * BA;
|
||||
// compute the nearest point on cube
|
||||
float maxBA = glm::max(glm::max(fabs(BA.x), fabs(BA.y)), fabs(BA.z));
|
||||
glm::vec3 surfaceB = cubeCenter - (0.5f * cubeSide / maxBA) * BA;
|
||||
// collision happens when "vector to surfaceA from surfaceB" dots with BA to produce a positive value
|
||||
glm::vec3 surfaceAB = surfaceA - surfaceB;
|
||||
if (glm::dot(surfaceAB, BA) > 0.f) {
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (collision) {
|
||||
/* KEEP THIS CODE -- this is how to collide the cube with stark face normals (no rounding).
|
||||
* We might want to use this code later for sealing boundaries between adjacent voxels.
|
||||
// penetration is parallel to box side direction
|
||||
BA /= maxBA;
|
||||
glm::vec3 direction;
|
||||
glm::modf(BA, direction);
|
||||
direction = glm::normalize(direction);
|
||||
*/
|
||||
|
||||
// For rounded normals at edges and corners:
|
||||
// At this point imagine that sphereCenter touches a "normalized" cube with rounded edges.
|
||||
// This cube has a sidelength of 2 and its smoothing radius is sphereRadius/maxBA.
|
||||
// We're going to try to compute the "negative normal" (and hence direction of penetration)
|
||||
// of this surface.
|
||||
|
||||
float radius = sphereRadius / (distance * maxBA); // normalized radius
|
||||
float shortLength = maxBA - radius;
|
||||
glm::vec3 direction = BA;
|
||||
if (shortLength > 0.0f) {
|
||||
direction = glm::abs(BA) - glm::vec3(shortLength);
|
||||
// Set any negative components to zero, and adopt the sign of the original BA component.
|
||||
// Unfortunately there isn't an easy way to make this fast.
|
||||
if (direction.x < 0.0f) {
|
||||
direction.x = 0.f;
|
||||
} else if (BA.x < 0.f) {
|
||||
direction.x = -direction.x;
|
||||
}
|
||||
if (direction.y < 0.0f) {
|
||||
direction.y = 0.f;
|
||||
} else if (BA.y < 0.f) {
|
||||
direction.y = -direction.y;
|
||||
}
|
||||
if (direction.z < 0.0f) {
|
||||
direction.z = 0.f;
|
||||
} else if (BA.z < 0.f) {
|
||||
direction.z = -direction.z;
|
||||
}
|
||||
}
|
||||
direction = glm::normalize(direction);
|
||||
|
||||
// penetration is the projection of surfaceAB on direction
|
||||
collision->_penetration = glm::dot(surfaceAB, direction) * direction;
|
||||
// contactPoint is on surface of A
|
||||
collision->_contactPoint = sphereCenter - sphereRadius * direction;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (sphereRadius + 0.5f * cubeSide > distance) {
|
||||
// NOTE: for cocentric approximation we collide sphere and cube as two spheres which means
|
||||
// this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON)
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (collision) {
|
||||
// the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis)
|
||||
collision->_penetration = (sphereRadius + 0.5f * cubeSide) * glm::vec3(0.0f, -1.0f, 0.0f);
|
||||
// contactPoint is on surface of A
|
||||
collision->_contactPoint = sphereCenter + collision->_penetration;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) {
|
||||
return sphereAACube(sphereA->getPosition(), sphereA->getRadius(), cubeCenter, cubeSide, collisions);
|
||||
}
|
||||
|
||||
bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) {
|
||||
// find nerest approach of capsule line segment to cube
|
||||
glm::vec3 capsuleAxis;
|
||||
capsuleA->computeNormalizedAxis(capsuleAxis);
|
||||
float offset = glm::dot(cubeCenter - capsuleA->getPosition(), capsuleAxis);
|
||||
float halfHeight = capsuleA->getHalfHeight();
|
||||
if (offset > halfHeight) {
|
||||
offset = halfHeight;
|
||||
} else if (offset < -halfHeight) {
|
||||
offset = -halfHeight;
|
||||
}
|
||||
glm::vec3 nearestApproach = capsuleA->getPosition() + offset * capsuleAxis;
|
||||
// collide nearest approach like a sphere at that point
|
||||
return sphereAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ShapeCollider
|
||||
|
|
|
@ -33,6 +33,13 @@ namespace ShapeCollider {
|
|||
/// \return true if any shapes collide
|
||||
bool collideShapesCoarse(const QVector<const Shape*>& shapesA, const QVector<const Shape*>& shapesB, CollisionInfo& collision);
|
||||
|
||||
/// \param shapeA a pointer to a shape
|
||||
/// \param cubeCenter center of cube
|
||||
/// \param cubeSide lenght of side of cube
|
||||
/// \param collisions[out] average collision details
|
||||
/// \return true if shapeA collides with axis aligned cube
|
||||
bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
||||
|
||||
/// \param sphereA pointer to first shape
|
||||
/// \param sphereB pointer to second shape
|
||||
/// \param[out] collisions where to append collision details
|
||||
|
@ -129,6 +136,20 @@ namespace ShapeCollider {
|
|||
/// \return true if shapes collide
|
||||
bool listList(const ListShape* listA, const ListShape* listB, CollisionList& collisions);
|
||||
|
||||
/// \param sphereA pointer to sphere
|
||||
/// \param cubeCenter center of cube
|
||||
/// \param cubeSide lenght of side of cube
|
||||
/// \param[out] collisions where to append collision details
|
||||
/// \return true if sphereA collides with axis aligned cube
|
||||
bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
||||
|
||||
/// \param capsuleA pointer to capsule
|
||||
/// \param cubeCenter center of cube
|
||||
/// \param cubeSide lenght of side of cube
|
||||
/// \param[out] collisions where to append collision details
|
||||
/// \return true if capsuleA collides with axis aligned cube
|
||||
bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
||||
|
||||
} // namespace ShapeCollider
|
||||
|
||||
#endif // hifi_ShapeCollider_h
|
||||
|
|
|
@ -15,28 +15,39 @@
|
|||
#include <cctype>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "OctalCode.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
quint64 usecTimestamp(const timeval *time) {
|
||||
return (time->tv_sec * 1000000 + time->tv_usec);
|
||||
}
|
||||
|
||||
int usecTimestampNowAdjust = 0;
|
||||
static int usecTimestampNowAdjust = 0; // in usec
|
||||
void usecTimestampNowForceClockSkew(int clockSkew) {
|
||||
::usecTimestampNowAdjust = clockSkew;
|
||||
}
|
||||
|
||||
quint64 usecTimestampNow() {
|
||||
timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
return (now.tv_sec * 1000000 + now.tv_usec) + ::usecTimestampNowAdjust;
|
||||
static bool usecTimestampNowIsInitialized = false;
|
||||
static qint64 TIME_REFERENCE = 0; // in usec
|
||||
static QElapsedTimer timestampTimer;
|
||||
|
||||
if (!usecTimestampNowIsInitialized) {
|
||||
TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec
|
||||
timestampTimer.start();
|
||||
usecTimestampNowIsInitialized = true;
|
||||
}
|
||||
|
||||
// usec nsec to usec usec
|
||||
return TIME_REFERENCE + timestampTimer.nsecsElapsed() / 1000 + ::usecTimestampNowAdjust;
|
||||
}
|
||||
|
||||
float randFloat() {
|
||||
|
@ -640,27 +651,59 @@ void debug::checkDeadBeef(void* memoryVoid, int size) {
|
|||
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
|
||||
glm::vec3 safeEulerAngles(const glm::quat& q) {
|
||||
float sy = 2.0f * (q.y * q.w - q.x * q.z);
|
||||
glm::vec3 eulers;
|
||||
if (sy < 1.0f - EPSILON) {
|
||||
if (sy > -1.0f + EPSILON) {
|
||||
return glm::vec3(
|
||||
eulers = glm::vec3(
|
||||
atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
|
||||
asinf(sy),
|
||||
atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)));
|
||||
|
||||
} else {
|
||||
// not a unique solution; x + z = atan2(-m21, m11)
|
||||
return glm::vec3(
|
||||
eulers = glm::vec3(
|
||||
0.0f,
|
||||
- PI_OVER_TWO,
|
||||
atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
|
||||
}
|
||||
} else {
|
||||
// not a unique solution; x - z = atan2(-m21, m11)
|
||||
return glm::vec3(
|
||||
eulers = glm::vec3(
|
||||
0.0f,
|
||||
PI_OVER_TWO,
|
||||
-atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
|
||||
}
|
||||
|
||||
// adjust so that z, rather than y, is in [-pi/2, pi/2]
|
||||
if (eulers.z < -PI_OVER_TWO) {
|
||||
if (eulers.x < 0.0f) {
|
||||
eulers.x += PI;
|
||||
} else {
|
||||
eulers.x -= PI;
|
||||
}
|
||||
eulers.y = -eulers.y;
|
||||
if (eulers.y < 0.0f) {
|
||||
eulers.y += PI;
|
||||
} else {
|
||||
eulers.y -= PI;
|
||||
}
|
||||
eulers.z += PI;
|
||||
|
||||
} else if (eulers.z > PI_OVER_TWO) {
|
||||
if (eulers.x < 0.0f) {
|
||||
eulers.x += PI;
|
||||
} else {
|
||||
eulers.x -= PI;
|
||||
}
|
||||
eulers.y = -eulers.y;
|
||||
if (eulers.y < 0.0f) {
|
||||
eulers.y += PI;
|
||||
} else {
|
||||
eulers.y -= PI;
|
||||
}
|
||||
eulers.z -= PI;
|
||||
}
|
||||
return eulers;
|
||||
}
|
||||
|
||||
// Helper function returns the positive angle (in radians) between two 3D vectors
|
||||
|
|
|
@ -24,12 +24,6 @@
|
|||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Systime.h"
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
const int BYTES_PER_COLOR = 3;
|
||||
const int BYTES_PER_FLAGS = 1;
|
||||
typedef unsigned char rgbColor[BYTES_PER_COLOR];
|
||||
|
@ -66,7 +60,6 @@ static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
|
|||
|
||||
const int BITS_IN_BYTE = 8;
|
||||
|
||||
quint64 usecTimestamp(const timeval *time);
|
||||
quint64 usecTimestampNow();
|
||||
void usecTimestampNowForceClockSkew(int clockSkew);
|
||||
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
//
|
||||
// Systime.cpp
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Copyright 2013 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
|
||||
//
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include "Systime.h"
|
||||
|
||||
/**
|
||||
* gettimeofday
|
||||
* Implementation according to:
|
||||
* The Open Group Base Specifications Issue 6
|
||||
* IEEE Std 1003.1, 2004 Edition
|
||||
*/
|
||||
|
||||
/**
|
||||
* THIS SOFTWARE IS NOT COPYRIGHTED
|
||||
*
|
||||
* This source code is offered for use in the public domain. You may
|
||||
* use, modify or distribute it freely.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful but
|
||||
* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
|
||||
* DISCLAIMED. This includes but is not limited to warranties of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* Contributed by:
|
||||
* Danny Smith <dannysmith@users.sourceforge.net>
|
||||
*/
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
/** Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
|
||||
#define _W32_FT_OFFSET (116444736000000000ULL)
|
||||
|
||||
int gettimeofday(timeval* p_tv, timezone* p_tz) {
|
||||
|
||||
union {
|
||||
unsigned long long ns100; /**time since 1 Jan 1601 in 100ns units */
|
||||
FILETIME ft;
|
||||
} _now;
|
||||
|
||||
if (p_tv) {
|
||||
GetSystemTimeAsFileTime (&_now.ft);
|
||||
p_tv->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL );
|
||||
p_tv->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL);
|
||||
}
|
||||
|
||||
/** Always return 0 as per Open Group Base Specifications Issue 6.
|
||||
Do not set errno on error. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,27 +0,0 @@
|
|||
//
|
||||
// Systime.h
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Copyright 2013 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
|
||||
//
|
||||
|
||||
#ifndef hifi_Systime_h
|
||||
#define hifi_Systime_h
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
struct timezone {
|
||||
int tz_minuteswest; /* minutes west of Greenwich */
|
||||
int tz_dsttime; /* type of dst correction */
|
||||
};
|
||||
|
||||
int gettimeofday(struct timeval* p_tv, struct timezone* p_tz);
|
||||
|
||||
#endif
|
||||
|
||||
#endif // hifi_Systime_h
|
|
@ -42,7 +42,11 @@ void VoxelTree::deleteVoxelAt(float x, float y, float z, float s) {
|
|||
}
|
||||
|
||||
VoxelTreeElement* VoxelTree::getVoxelAt(float x, float y, float z, float s) const {
|
||||
return (VoxelTreeElement*)getOctreeElementAt(x, y, z, s);
|
||||
return static_cast<VoxelTreeElement*>(getOctreeElementAt(x, y, z, s));
|
||||
}
|
||||
|
||||
VoxelTreeElement* VoxelTree::getEnclosingVoxelAt(float x, float y, float z, float s) const {
|
||||
return static_cast<VoxelTreeElement*>(getOctreeEnclosingElementAt(x, y, z, s));
|
||||
}
|
||||
|
||||
void VoxelTree::createVoxel(float x, float y, float z, float s,
|
||||
|
|
|
@ -29,7 +29,15 @@ public:
|
|||
VoxelTreeElement* getRoot() { return (VoxelTreeElement*)_rootNode; }
|
||||
|
||||
void deleteVoxelAt(float x, float y, float z, float s);
|
||||
|
||||
/// Find the voxel at position x,y,z,s
|
||||
/// \return pointer to the VoxelTreeElement or NULL if none at x,y,z,s.
|
||||
VoxelTreeElement* getVoxelAt(float x, float y, float z, float s) const;
|
||||
|
||||
/// Find the voxel at position x,y,z,s
|
||||
/// \return pointer to the VoxelTreeElement or to the smallest enclosing parent if none at x,y,z,s.
|
||||
VoxelTreeElement* getEnclosingVoxelAt(float x, float y, float z, float s) const;
|
||||
|
||||
void createVoxel(float x, float y, float z, float s,
|
||||
unsigned char red, unsigned char green, unsigned char blue, bool destructive = false);
|
||||
|
||||
|
|
|
@ -13,6 +13,50 @@
|
|||
|
||||
#include "VoxelTreeCommands.h"
|
||||
|
||||
|
||||
|
||||
struct SendVoxelsOperationArgs {
|
||||
const unsigned char* newBaseOctCode;
|
||||
VoxelEditPacketSender* packetSender;
|
||||
};
|
||||
|
||||
bool sendVoxelsOperation(OctreeElement* element, void* extraData) {
|
||||
VoxelTreeElement* voxel = static_cast<VoxelTreeElement*>(element);
|
||||
SendVoxelsOperationArgs* args = static_cast<SendVoxelsOperationArgs*>(extraData);
|
||||
if (voxel->isColored()) {
|
||||
const unsigned char* nodeOctalCode = voxel->getOctalCode();
|
||||
unsigned char* codeColorBuffer = NULL;
|
||||
int codeLength = 0;
|
||||
int bytesInCode = 0;
|
||||
int codeAndColorLength;
|
||||
|
||||
// If the newBase is NULL, then don't rebase
|
||||
if (args->newBaseOctCode) {
|
||||
codeColorBuffer = rebaseOctalCode(nodeOctalCode, args->newBaseOctCode, true);
|
||||
codeLength = numberOfThreeBitSectionsInCode(codeColorBuffer);
|
||||
bytesInCode = bytesRequiredForCodeLength(codeLength);
|
||||
codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA;
|
||||
} else {
|
||||
codeLength = numberOfThreeBitSectionsInCode(nodeOctalCode);
|
||||
bytesInCode = bytesRequiredForCodeLength(codeLength);
|
||||
codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA;
|
||||
codeColorBuffer = new unsigned char[codeAndColorLength];
|
||||
memcpy(codeColorBuffer, nodeOctalCode, bytesInCode);
|
||||
}
|
||||
|
||||
// copy the colors over
|
||||
codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX];
|
||||
codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX];
|
||||
codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX];
|
||||
args->packetSender->queueVoxelEditMessage(PacketTypeVoxelSetDestructive,
|
||||
codeColorBuffer, codeAndColorLength);
|
||||
|
||||
delete[] codeColorBuffer;
|
||||
}
|
||||
return true; // keep going
|
||||
}
|
||||
|
||||
|
||||
AddVoxelCommand::AddVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender, QUndoCommand* parent) :
|
||||
QUndoCommand("Add Voxel", parent),
|
||||
_tree(tree),
|
||||
|
@ -43,11 +87,40 @@ DeleteVoxelCommand::DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, Voxe
|
|||
QUndoCommand("Delete Voxel", parent),
|
||||
_tree(tree),
|
||||
_packetSender(packetSender),
|
||||
_voxel(voxel)
|
||||
_voxel(voxel),
|
||||
_oldTree(NULL)
|
||||
{
|
||||
_tree->lockForRead();
|
||||
VoxelTreeElement* element = _tree->getEnclosingVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
||||
if (element->getScale() == _voxel.s) {
|
||||
if (!element->hasContent() && !element->isLeaf()) {
|
||||
_oldTree = new VoxelTree();
|
||||
_tree->copySubTreeIntoNewTree(element, _oldTree, false);
|
||||
} else {
|
||||
_voxel.red = element->getColor()[0];
|
||||
_voxel.green = element->getColor()[1];
|
||||
_voxel.blue = element->getColor()[2];
|
||||
}
|
||||
} else if (element->hasContent() && element->isLeaf()) {
|
||||
_voxel.red = element->getColor()[0];
|
||||
_voxel.green = element->getColor()[1];
|
||||
_voxel.blue = element->getColor()[2];
|
||||
} else {
|
||||
_voxel.s = 0.0f;
|
||||
qDebug() << "No element for delete.";
|
||||
}
|
||||
_tree->unlock();
|
||||
}
|
||||
|
||||
DeleteVoxelCommand::~DeleteVoxelCommand() {
|
||||
delete _oldTree;
|
||||
}
|
||||
|
||||
void DeleteVoxelCommand::redo() {
|
||||
if (_voxel.s == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tree) {
|
||||
_tree->deleteVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
||||
}
|
||||
|
@ -57,10 +130,32 @@ void DeleteVoxelCommand::redo() {
|
|||
}
|
||||
|
||||
void DeleteVoxelCommand::undo() {
|
||||
if (_tree) {
|
||||
_tree->createVoxel(_voxel.x, _voxel.y, _voxel.z, _voxel.s, _voxel.red, _voxel.green, _voxel.blue);
|
||||
if (_voxel.s == 0.0f) {
|
||||
return;
|
||||
}
|
||||
if (_packetSender) {
|
||||
_packetSender->queueVoxelEditMessages(PacketTypeVoxelSet, 1, &_voxel);
|
||||
|
||||
if (_oldTree) {
|
||||
VoxelTreeElement* element = _oldTree->getVoxelAt(_voxel.x, _voxel.y, _voxel.z, _voxel.s);
|
||||
if (element) {
|
||||
if (_tree) {
|
||||
_tree->lockForWrite();
|
||||
_oldTree->copySubTreeIntoNewTree(element, _tree, false);
|
||||
_tree->unlock();
|
||||
}
|
||||
if (_packetSender) {
|
||||
SendVoxelsOperationArgs args;
|
||||
args.newBaseOctCode = NULL;
|
||||
args.packetSender = _packetSender;
|
||||
_oldTree->recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
_packetSender->releaseQueuedMessages();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_tree) {
|
||||
_tree->createVoxel(_voxel.x, _voxel.y, _voxel.z, _voxel.s, _voxel.red, _voxel.green, _voxel.blue);
|
||||
}
|
||||
if (_packetSender) {
|
||||
_packetSender->queueVoxelEditMessages(PacketTypeVoxelSet, 1, &_voxel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ private:
|
|||
class DeleteVoxelCommand : public QUndoCommand {
|
||||
public:
|
||||
DeleteVoxelCommand(VoxelTree* tree, VoxelDetail& voxel, VoxelEditPacketSender* packetSender = NULL, QUndoCommand* parent = NULL);
|
||||
~DeleteVoxelCommand();
|
||||
|
||||
virtual void redo();
|
||||
virtual void undo();
|
||||
|
@ -44,6 +45,7 @@ private:
|
|||
VoxelTree* _tree;
|
||||
VoxelEditPacketSender* _packetSender;
|
||||
VoxelDetail _voxel;
|
||||
VoxelTree* _oldTree;
|
||||
};
|
||||
|
||||
#endif // hifi_VoxelTreeCommands_h
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
|
||||
#include <QReadWriteLock>
|
||||
|
||||
#include <AABox.h>
|
||||
#include <OctreeElement.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "AABox.h"
|
||||
#include "ViewFrustum.h"
|
||||
#include "VoxelConstants.h"
|
||||
|
||||
|
|
|
@ -76,32 +76,16 @@ void VoxelsScriptingInterface::setVoxel(float x, float y, float z, float scale,
|
|||
if (_tree) {
|
||||
if (_undoStack) {
|
||||
AddVoxelCommand* addCommand = new AddVoxelCommand(_tree,
|
||||
addVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
|
||||
VoxelTreeElement* deleteVoxelElement = _tree->getVoxelAt(addVoxelDetail.x, addVoxelDetail.y, addVoxelDetail.z, addVoxelDetail.s);
|
||||
if (deleteVoxelElement) {
|
||||
nodeColor color;
|
||||
memcpy(&color, &deleteVoxelElement->getColor(), sizeof(nodeColor));
|
||||
VoxelDetail deleteVoxelDetail = {addVoxelDetail.x,
|
||||
addVoxelDetail.y,
|
||||
addVoxelDetail.z,
|
||||
addVoxelDetail.s,
|
||||
color[0],
|
||||
color[1],
|
||||
color[2]};
|
||||
DeleteVoxelCommand* delCommand = new DeleteVoxelCommand(_tree,
|
||||
deleteVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
_undoStack->beginMacro(addCommand->text());
|
||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
||||
_undoStack->push(delCommand);
|
||||
_undoStack->push(addCommand);
|
||||
_undoStack->endMacro();
|
||||
} else {
|
||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
||||
_undoStack->push(addCommand);
|
||||
}
|
||||
addVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
DeleteVoxelCommand* deleteCommand = new DeleteVoxelCommand(_tree,
|
||||
addVoxelDetail,
|
||||
getVoxelPacketSender());
|
||||
_undoStack->beginMacro(addCommand->text());
|
||||
// As QUndoStack automatically executes redo() on push, we don't need to execute the command ourselves.
|
||||
_undoStack->push(deleteCommand);
|
||||
_undoStack->push(addCommand);
|
||||
_undoStack->endMacro();
|
||||
} else {
|
||||
// queue the destructive add
|
||||
queueVoxelAdd(PacketTypeVoxelSetDestructive, addVoxelDetail);
|
||||
|
|
|
@ -23,18 +23,18 @@
|
|||
|
||||
#include "ShapeColliderTests.h"
|
||||
|
||||
const glm::vec3 origin(0.f);
|
||||
static const glm::vec3 xAxis(1.f, 0.f, 0.f);
|
||||
static const glm::vec3 yAxis(0.f, 1.f, 0.f);
|
||||
static const glm::vec3 zAxis(0.f, 0.f, 1.f);
|
||||
const glm::vec3 origin(0.0f);
|
||||
static const glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||
static const glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
static const glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
||||
|
||||
void ShapeColliderTests::sphereMissesSphere() {
|
||||
// non-overlapping spheres of unequal size
|
||||
float radiusA = 7.f;
|
||||
float radiusB = 3.f;
|
||||
float radiusA = 7.0f;
|
||||
float radiusB = 3.0f;
|
||||
float alpha = 1.2f;
|
||||
float beta = 1.3f;
|
||||
glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.f, 2.f, 3.f));
|
||||
glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
|
||||
float offsetDistance = alpha * radiusA + beta * radiusB;
|
||||
|
||||
SphereShape sphereA(radiusA, origin);
|
||||
|
@ -77,13 +77,13 @@ void ShapeColliderTests::sphereMissesSphere() {
|
|||
|
||||
void ShapeColliderTests::sphereTouchesSphere() {
|
||||
// overlapping spheres of unequal size
|
||||
float radiusA = 7.f;
|
||||
float radiusB = 3.f;
|
||||
float radiusA = 7.0f;
|
||||
float radiusB = 3.0f;
|
||||
float alpha = 0.2f;
|
||||
float beta = 0.3f;
|
||||
glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.f, 2.f, 3.f));
|
||||
glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
|
||||
float offsetDistance = alpha * radiusA + beta * radiusB;
|
||||
float expectedPenetrationDistance = (1.f - alpha) * radiusA + (1.f - beta) * radiusB;
|
||||
float expectedPenetrationDistance = (1.0f - alpha) * radiusA + (1.0f - beta) * radiusB;
|
||||
glm::vec3 expectedPenetration = expectedPenetrationDistance * offsetDirection;
|
||||
|
||||
SphereShape sphereA(radiusA, origin);
|
||||
|
@ -118,8 +118,7 @@ void ShapeColliderTests::sphereTouchesSphere() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad penetration: expected = " << expectedPenetration
|
||||
<< " actual = " << collision->_penetration
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_penetration;
|
||||
}
|
||||
|
||||
// contactPoint is on surface of sphereA
|
||||
|
@ -129,8 +128,7 @@ void ShapeColliderTests::sphereTouchesSphere() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
|
||||
<< " actual = " << collision->_contactPoint
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_contactPoint;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,8 +148,7 @@ void ShapeColliderTests::sphereTouchesSphere() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad penetration: expected = " << expectedPenetration
|
||||
<< " actual = " << collision->_penetration
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_penetration;
|
||||
}
|
||||
|
||||
// contactPoint is on surface of sphereA
|
||||
|
@ -161,8 +158,7 @@ void ShapeColliderTests::sphereTouchesSphere() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
|
||||
<< " actual = " << collision->_contactPoint
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_contactPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +177,7 @@ void ShapeColliderTests::sphereMissesCapsule() {
|
|||
|
||||
// give the capsule some arbirary transform
|
||||
float angle = 37.8f;
|
||||
glm::vec3 axis = glm::normalize( glm::vec3(-7.f, 2.8f, 9.3f) );
|
||||
glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) );
|
||||
glm::quat rotation = glm::angleAxis(angle, axis);
|
||||
glm::vec3 translation(15.1f, -27.1f, -38.6f);
|
||||
capsuleB.setRotation(rotation);
|
||||
|
@ -190,7 +186,7 @@ void ShapeColliderTests::sphereMissesCapsule() {
|
|||
CollisionList collisions(16);
|
||||
|
||||
// walk sphereA along the local yAxis next to, but not touching, capsuleB
|
||||
glm::vec3 localStartPosition(radialOffset, axialOffset, 0.f);
|
||||
glm::vec3 localStartPosition(radialOffset, axialOffset, 0.0f);
|
||||
int numberOfSteps = 10;
|
||||
float delta = 1.3f * (totalRadius + halfHeightB) / (numberOfSteps - 1);
|
||||
for (int i = 0; i < numberOfSteps; ++i) {
|
||||
|
@ -224,10 +220,10 @@ void ShapeColliderTests::sphereMissesCapsule() {
|
|||
|
||||
void ShapeColliderTests::sphereTouchesCapsule() {
|
||||
// overlapping sphere and capsule
|
||||
float radiusA = 2.f;
|
||||
float radiusB = 1.f;
|
||||
float radiusA = 2.0f;
|
||||
float radiusB = 1.0f;
|
||||
float totalRadius = radiusA + radiusB;
|
||||
float halfHeightB = 2.f;
|
||||
float halfHeightB = 2.0f;
|
||||
float alpha = 0.5f;
|
||||
float beta = 0.5f;
|
||||
float radialOffset = alpha * radiusA + beta * radiusB;
|
||||
|
@ -257,8 +253,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad penetration: expected = " << expectedPenetration
|
||||
<< " actual = " << collision->_penetration
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_penetration;
|
||||
}
|
||||
|
||||
// contactPoint is on surface of sphereA
|
||||
|
@ -267,8 +262,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
|
||||
<< " actual = " << collision->_contactPoint
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_contactPoint;
|
||||
}
|
||||
|
||||
// capsuleB collides with sphereA
|
||||
|
@ -288,8 +282,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad penetration: expected = " << expectedPenetration
|
||||
<< " actual = " << collision->_penetration
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_penetration;
|
||||
}
|
||||
|
||||
// contactPoint is on surface of capsuleB
|
||||
|
@ -300,8 +293,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
|
||||
<< " actual = " << collision->_contactPoint
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_contactPoint;
|
||||
}
|
||||
}
|
||||
{ // sphereA hits end cap at axis
|
||||
|
@ -319,13 +311,12 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
|
||||
// penetration points from sphereA into capsuleB
|
||||
CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
|
||||
glm::vec3 expectedPenetration = - ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
|
||||
glm::vec3 expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis;
|
||||
float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
|
||||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad penetration: expected = " << expectedPenetration
|
||||
<< " actual = " << collision->_penetration
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_penetration;
|
||||
}
|
||||
|
||||
// contactPoint is on surface of sphereA
|
||||
|
@ -334,8 +325,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
|
||||
<< " actual = " << collision->_contactPoint
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_contactPoint;
|
||||
}
|
||||
|
||||
// capsuleB collides with sphereA
|
||||
|
@ -350,13 +340,12 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
|
||||
// penetration points from sphereA into capsuleB
|
||||
collision = collisions.getCollision(numCollisions - 1);
|
||||
expectedPenetration = ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
|
||||
expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis;
|
||||
inaccuracy = glm::length(collision->_penetration - expectedPenetration);
|
||||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad penetration: expected = " << expectedPenetration
|
||||
<< " actual = " << collision->_penetration
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_penetration;
|
||||
}
|
||||
|
||||
// contactPoint is on surface of capsuleB
|
||||
|
@ -367,8 +356,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
|
||||
<< " actual = " << collision->_contactPoint
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_contactPoint;
|
||||
}
|
||||
}
|
||||
{ // sphereA hits start cap at axis
|
||||
|
@ -386,13 +374,12 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
|
||||
// penetration points from sphereA into capsuleB
|
||||
CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
|
||||
glm::vec3 expectedPenetration = ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
|
||||
glm::vec3 expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis;
|
||||
float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
|
||||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad penetration: expected = " << expectedPenetration
|
||||
<< " actual = " << collision->_penetration
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_penetration;
|
||||
}
|
||||
|
||||
// contactPoint is on surface of sphereA
|
||||
|
@ -401,8 +388,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
|
||||
<< " actual = " << collision->_contactPoint
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_contactPoint;
|
||||
}
|
||||
|
||||
// capsuleB collides with sphereA
|
||||
|
@ -417,13 +403,12 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
|
||||
// penetration points from sphereA into capsuleB
|
||||
collision = collisions.getCollision(numCollisions - 1);
|
||||
expectedPenetration = - ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
|
||||
expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis;
|
||||
inaccuracy = glm::length(collision->_penetration - expectedPenetration);
|
||||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad penetration: expected = " << expectedPenetration
|
||||
<< " actual = " << collision->_penetration
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_penetration;
|
||||
}
|
||||
|
||||
// contactPoint is on surface of capsuleB
|
||||
|
@ -434,8 +419,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
|
||||
<< " actual = " << collision->_contactPoint
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_contactPoint;
|
||||
}
|
||||
}
|
||||
if (collisions.size() != numCollisions) {
|
||||
|
@ -447,10 +431,10 @@ void ShapeColliderTests::sphereTouchesCapsule() {
|
|||
|
||||
void ShapeColliderTests::capsuleMissesCapsule() {
|
||||
// non-overlapping capsules
|
||||
float radiusA = 2.f;
|
||||
float halfHeightA = 3.f;
|
||||
float radiusB = 3.f;
|
||||
float halfHeightB = 4.f;
|
||||
float radiusA = 2.0f;
|
||||
float halfHeightA = 3.0f;
|
||||
float radiusB = 3.0f;
|
||||
float halfHeightB = 4.0f;
|
||||
|
||||
float totalRadius = radiusA + radiusB;
|
||||
float totalHalfLength = totalRadius + halfHeightA + halfHeightB;
|
||||
|
@ -516,10 +500,10 @@ void ShapeColliderTests::capsuleMissesCapsule() {
|
|||
|
||||
void ShapeColliderTests::capsuleTouchesCapsule() {
|
||||
// overlapping capsules
|
||||
float radiusA = 2.f;
|
||||
float halfHeightA = 3.f;
|
||||
float radiusB = 3.f;
|
||||
float halfHeightB = 4.f;
|
||||
float radiusA = 2.0f;
|
||||
float halfHeightA = 3.0f;
|
||||
float radiusB = 3.0f;
|
||||
float halfHeightB = 4.0f;
|
||||
|
||||
float totalRadius = radiusA + radiusB;
|
||||
float totalHalfLength = totalRadius + halfHeightA + halfHeightB;
|
||||
|
@ -617,8 +601,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad penetration: expected = " << expectedPenetration
|
||||
<< " actual = " << collision->_penetration
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_penetration;
|
||||
}
|
||||
|
||||
glm::vec3 expectedContactPoint = capsuleA.getPosition() + radiusA * xAxis;
|
||||
|
@ -626,8 +609,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() {
|
|||
if (fabs(inaccuracy) > EPSILON) {
|
||||
std::cout << __FILE__ << ":" << __LINE__
|
||||
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
|
||||
<< " actual = " << collision->_contactPoint
|
||||
<< std::endl;
|
||||
<< " actual = " << collision->_contactPoint;
|
||||
}
|
||||
|
||||
// capsuleB vs capsuleA
|
||||
|
@ -699,6 +681,116 @@ void ShapeColliderTests::capsuleTouchesCapsule() {
|
|||
}
|
||||
}
|
||||
|
||||
void ShapeColliderTests::sphereTouchesAACube() {
|
||||
CollisionList collisions(16);
|
||||
|
||||
glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f);
|
||||
float cubeSide = 2.0f;
|
||||
|
||||
float sphereRadius = 1.0f;
|
||||
glm::vec3 sphereCenter(0.0f);
|
||||
SphereShape sphere(sphereRadius, sphereCenter);
|
||||
|
||||
float sphereOffset = (0.5f * cubeSide + sphereRadius - 0.25f);
|
||||
|
||||
// top
|
||||
sphereCenter = cubeCenter + sphereOffset * yAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// bottom
|
||||
sphereCenter = cubeCenter - sphereOffset * yAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// left
|
||||
sphereCenter = cubeCenter + sphereOffset * xAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// right
|
||||
sphereCenter = cubeCenter - sphereOffset * xAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// forward
|
||||
sphereCenter = cubeCenter + sphereOffset * zAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// back
|
||||
sphereCenter = cubeCenter - sphereOffset * zAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeColliderTests::sphereMissesAACube() {
|
||||
CollisionList collisions(16);
|
||||
|
||||
glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f);
|
||||
float cubeSide = 2.0f;
|
||||
|
||||
float sphereRadius = 1.0f;
|
||||
glm::vec3 sphereCenter(0.0f);
|
||||
SphereShape sphere(sphereRadius, sphereCenter);
|
||||
|
||||
float sphereOffset = (0.5f * cubeSide + sphereRadius + 0.25f);
|
||||
|
||||
// top
|
||||
sphereCenter = cubeCenter + sphereOffset * yAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// bottom
|
||||
sphereCenter = cubeCenter - sphereOffset * yAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// left
|
||||
sphereCenter = cubeCenter + sphereOffset * xAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// right
|
||||
sphereCenter = cubeCenter - sphereOffset * xAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// forward
|
||||
sphereCenter = cubeCenter + sphereOffset * zAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
|
||||
}
|
||||
|
||||
// back
|
||||
sphereCenter = cubeCenter - sphereOffset * zAxis;
|
||||
sphere.setPosition(sphereCenter);
|
||||
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
|
||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ShapeColliderTests::runAllTests() {
|
||||
sphereMissesSphere();
|
||||
|
@ -709,4 +801,7 @@ void ShapeColliderTests::runAllTests() {
|
|||
|
||||
capsuleMissesCapsule();
|
||||
capsuleTouchesCapsule();
|
||||
|
||||
sphereTouchesAACube();
|
||||
sphereMissesAACube();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ namespace ShapeColliderTests {
|
|||
void capsuleMissesCapsule();
|
||||
void capsuleTouchesCapsule();
|
||||
|
||||
void sphereTouchesAACube();
|
||||
void sphereMissesAACube();
|
||||
|
||||
void runAllTests();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue