Merge branch 'master' of https://github.com/worklist/hifi into 19495

Conflicts:
	interface/interface_en.ts
This commit is contained in:
stojce 2014-03-12 23:03:26 +01:00
commit 004e8e02d5
86 changed files with 2108 additions and 863 deletions

View file

@ -5,6 +5,7 @@ if (WIN32)
endif (WIN32)
project(hifi)
add_definitions(-DGLM_FORCE_RADIANS)
if (WIN32)
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)

View file

@ -78,9 +78,9 @@ glm::vec3 bugDirection = glm::vec3(0, 0, 1);
const int VOXELS_PER_BUG = 18;
glm::vec3 bugPathCenter = glm::vec3(0.25f,0.15f,0.25f); // glm::vec3(BUG_VOXEL_SIZE * 150.0, BUG_VOXEL_SIZE * 30.0, BUG_VOXEL_SIZE * 150.0);
float bugPathRadius = 0.2f; //BUG_VOXEL_SIZE * 140.0;
float bugPathTheta = 0.0 * PI_OVER_180;
float bugRotation = 0.0 * PI_OVER_180;
float bugAngleDelta = 0.2 * PI_OVER_180;
float bugPathTheta = 0.0f * RADIANS_PER_DEGREE;
float bugRotation = 0.0f * RADIANS_PER_DEGREE;
float bugAngleDelta = 0.2f * RADIANS_PER_DEGREE;
bool moveBugInLine = false;
class BugPart {
@ -160,11 +160,11 @@ static void renderMovingBug() {
bugPosition.z += (bugDirection.z * BUG_VOXEL_SIZE);
// Check boundaries
if (bugPosition.z > 1.0) {
bugDirection.z = -1;
if (bugPosition.z > 1.0f) {
bugDirection.z = -1.f;
}
if (bugPosition.z < BUG_VOXEL_SIZE) {
bugDirection.z = 1;
bugDirection.z = 1.f;
}
} else {
@ -174,9 +174,9 @@ static void renderMovingBug() {
bugRotation -= bugAngleDelta; // rotate slightly
// If we loop past end of circle, just reset back into normal range
if (bugPathTheta > (360.0f * PI_OVER_180)) {
bugPathTheta = 0;
bugRotation = 0;
if (bugPathTheta > TWO_PI) {
bugPathTheta = 0.f;
bugRotation = 0.f;
}
float x = bugPathCenter.x + bugPathRadius * cos(bugPathTheta);
@ -225,7 +225,7 @@ static void sendVoxelBlinkMessage() {
VoxelDetail detail;
detail.s = BEACON_SIZE;
glm::vec3 position = glm::vec3(0, 0, detail.s);
glm::vec3 position = glm::vec3(0.f, 0.f, detail.s);
detail.x = detail.s * floor(position.x / detail.s);
detail.y = detail.s * floor(position.y / detail.s);

View file

@ -114,7 +114,7 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
const float OFF_AXIS_ATTENUATION_FORMULA_STEP = (1 - MAX_OFF_AXIS_ATTENUATION) / 2.0f;
float offAxisCoefficient = MAX_OFF_AXIS_ATTENUATION +
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / 90.0f));
(OFF_AXIS_ATTENUATION_FORMULA_STEP * (angleOfDelivery / PI_OVER_TWO));
// multiply the current attenuation coefficient by the calculated off axis coefficient
attenuationCoefficient *= offAxisCoefficient;
@ -148,7 +148,7 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf
// figure out the number of samples of delay and the ratio of the amplitude
// in the weak channel for audio spatialization
float sinRatio = fabsf(sinf(glm::radians(bearingRelativeAngleToSource)));
float sinRatio = fabsf(sinf(bearingRelativeAngleToSource));
numSamplesDelay = PHASE_DELAY_AT_90 * sinRatio;
weakChannelAmplitudeRatio = 1 - (PHASE_AMPLITUDE_RATIO_AT_90 * sinRatio);
}

View file

@ -25,7 +25,7 @@ MetavoxelServer::MetavoxelServer(const QByteArray& packet) :
}
void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) {
edit.apply(_data);
edit.apply(_data, SharedObject::getWeakHash());
}
const QString METAVOXEL_SERVER_LOGGING_NAME = "metavoxel-server";
@ -111,13 +111,17 @@ int MetavoxelSession::parseData(const QByteArray& packet) {
}
void MetavoxelSession::sendDelta() {
// wait until we have a valid lod
if (!_lod.isValid()) {
return;
}
Bitstream& out = _sequencer.startPacket();
out << QVariant::fromValue(MetavoxelDeltaMessage());
_server->getData().writeDelta(_sendRecords.first().data, out);
_server->getData().writeDelta(_sendRecords.first().data, _sendRecords.first().lod, out, _lod);
_sequencer.endPacket();
// record the send
SendRecord record = { _sequencer.getOutgoingPacketNumber(), _server->getData() };
SendRecord record = { _sequencer.getOutgoingPacketNumber(), _server->getData(), _lod };
_sendRecords.append(record);
}
@ -139,7 +143,7 @@ void MetavoxelSession::handleMessage(const QVariant& message) {
int userType = message.userType();
if (userType == ClientStateMessage::Type) {
ClientStateMessage state = message.value<ClientStateMessage>();
_position = state.position;
_lod = state.lod;
} else if (userType == MetavoxelEditMessage::Type) {
_server->applyEdit(message.value<MetavoxelEditMessage>());

View file

@ -78,6 +78,7 @@ private:
public:
int packetNumber;
MetavoxelData data;
MetavoxelLOD lod;
};
MetavoxelServer* _server;
@ -86,7 +87,7 @@ private:
SharedNodePointer _node;
glm::vec3 _position;
MetavoxelLOD _lod;
QList<SendRecord> _sendRecords;
};

View file

@ -153,7 +153,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_touchAvgY(0.0f),
_isTouchPressed(false),
_mousePressed(false),
_chatEntryOn(false),
_audio(&_audioScope, STARTUP_JITTER_SAMPLES),
_enableProcessVoxelsThread(true),
_voxelProcessor(),
@ -507,7 +506,7 @@ void Application::paintGL() {
float headHeight = _myAvatar->getHead()->calculateAverageEyePosition().y - _myAvatar->getPosition().y;
_myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale());
_myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight, 0));
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
_myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
}
// Update camera position
@ -698,21 +697,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
if (activeWindow() == _window) {
if (_chatEntryOn) {
if (_chatEntry.keyPressEvent(event)) {
_myAvatar->setKeyState(event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete ?
DELETE_KEY_DOWN : INSERT_KEY_DOWN);
_myAvatar->setChatMessage(string(_chatEntry.getContents().size(), SOLID_BLOCK_CHAR));
} else {
_myAvatar->setChatMessage(_chatEntry.getContents());
_chatEntry.clear();
_chatEntryOn = false;
setMenuShortcutsEnabled(true);
}
return;
}
bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier);
bool isMeta = event->modifiers().testFlag(Qt::ControlModifier);
switch (event->key()) {
@ -744,7 +728,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (!_myAvatar->getDriveKeys(UP)) {
_myAvatar->jump();
}
_myAvatar->setDriveKeys(UP, 1);
_myAvatar->setDriveKeys(UP, 1.f);
break;
case Qt::Key_Asterisk:
@ -752,11 +736,11 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_C:
_myAvatar->setDriveKeys(DOWN, 1);
_myAvatar->setDriveKeys(DOWN, 1.f);
break;
case Qt::Key_W:
_myAvatar->setDriveKeys(FWD, 1);
_myAvatar->setDriveKeys(FWD, 1.f);
break;
case Qt::Key_S:
@ -765,7 +749,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} else if (!isShifted && isMeta) {
takeSnapshot();
} else {
_myAvatar->setDriveKeys(BACK, 1);
_myAvatar->setDriveKeys(BACK, 1.f);
}
break;
@ -783,36 +767,33 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::Atmosphere);
} else {
_myAvatar->setDriveKeys(ROT_LEFT, 1);
_myAvatar->setDriveKeys(ROT_LEFT, 1.f);
}
break;
case Qt::Key_D:
_myAvatar->setDriveKeys(ROT_RIGHT, 1);
_myAvatar->setDriveKeys(ROT_RIGHT, 1.f);
break;
case Qt::Key_Return:
case Qt::Key_Enter:
_chatEntryOn = true;
_myAvatar->setKeyState(NO_KEY_DOWN);
_myAvatar->setChatMessage(string());
setMenuShortcutsEnabled(false);
Menu::getInstance()->triggerOption(MenuOption::Chat);
break;
case Qt::Key_Up:
_myAvatar->setDriveKeys(isShifted ? UP : FWD, 1);
_myAvatar->setDriveKeys(isShifted ? UP : FWD, 1.f);
break;
case Qt::Key_Down:
_myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1);
_myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1.f);
break;
case Qt::Key_Left:
_myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1);
_myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1.f);
break;
case Qt::Key_Right:
_myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1);
_myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1.f);
break;
case Qt::Key_I:
@ -939,54 +920,49 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
if (activeWindow() == _window) {
if (_chatEntryOn) {
_myAvatar->setKeyState(NO_KEY_DOWN);
return;
}
switch (event->key()) {
case Qt::Key_E:
_myAvatar->setDriveKeys(UP, 0);
_myAvatar->setDriveKeys(UP, 0.f);
break;
case Qt::Key_C:
_myAvatar->setDriveKeys(DOWN, 0);
_myAvatar->setDriveKeys(DOWN, 0.f);
break;
case Qt::Key_W:
_myAvatar->setDriveKeys(FWD, 0);
_myAvatar->setDriveKeys(FWD, 0.f);
break;
case Qt::Key_S:
_myAvatar->setDriveKeys(BACK, 0);
_myAvatar->setDriveKeys(BACK, 0.f);
break;
case Qt::Key_A:
_myAvatar->setDriveKeys(ROT_LEFT, 0);
_myAvatar->setDriveKeys(ROT_LEFT, 0.f);
break;
case Qt::Key_D:
_myAvatar->setDriveKeys(ROT_RIGHT, 0);
_myAvatar->setDriveKeys(ROT_RIGHT, 0.f);
break;
case Qt::Key_Up:
_myAvatar->setDriveKeys(FWD, 0);
_myAvatar->setDriveKeys(UP, 0);
_myAvatar->setDriveKeys(FWD, 0.f);
_myAvatar->setDriveKeys(UP, 0.f);
break;
case Qt::Key_Down:
_myAvatar->setDriveKeys(BACK, 0);
_myAvatar->setDriveKeys(DOWN, 0);
_myAvatar->setDriveKeys(BACK, 0.f);
_myAvatar->setDriveKeys(DOWN, 0.f);
break;
case Qt::Key_Left:
_myAvatar->setDriveKeys(LEFT, 0);
_myAvatar->setDriveKeys(ROT_LEFT, 0);
_myAvatar->setDriveKeys(LEFT, 0.f);
_myAvatar->setDriveKeys(ROT_LEFT, 0.f);
break;
case Qt::Key_Right:
_myAvatar->setDriveKeys(RIGHT, 0);
_myAvatar->setDriveKeys(ROT_RIGHT, 0);
_myAvatar->setDriveKeys(RIGHT, 0.f);
_myAvatar->setDriveKeys(ROT_RIGHT, 0.f);
break;
default:
@ -1241,7 +1217,9 @@ void Application::idle() {
_idleLoopStdev.reset();
}
if (Menu::getInstance()->isOptionChecked(MenuOption::BuckyBalls)) {
_buckyBalls.simulate(timeSinceLastUpdate / 1000.f, Application::getInstance()->getAvatar()->getHandData());
}
// After finishing all of the above work, restart the idle timer, allowing 2ms to process events.
idleTimer->start(2);
@ -1486,7 +1464,7 @@ void Application::init() {
3.0f * TREE_SCALE / 2.0f));
_sharedVoxelSystemViewFrustum.setNearClip(TREE_SCALE / 2.0f);
_sharedVoxelSystemViewFrustum.setFarClip(3.0f * TREE_SCALE / 2.0f);
_sharedVoxelSystemViewFrustum.setFieldOfView(90);
_sharedVoxelSystemViewFrustum.setFieldOfView(90.f);
_sharedVoxelSystemViewFrustum.setOrientation(glm::quat());
_sharedVoxelSystemViewFrustum.calculate();
_sharedVoxelSystem.setViewFrustum(&_sharedVoxelSystemViewFrustum);
@ -2116,7 +2094,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
// We will use these below, from either the camera or head vectors calculated above
glm::vec3 position(camera.getPosition());
float fov = camera.getFieldOfView();
float fov = camera.getFieldOfView(); // degrees
float nearClip = camera.getNearClip();
float farClip = camera.getFarClip();
float aspectRatio = camera.getAspectRatio();
@ -2129,7 +2107,7 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
// Also make sure it's got the correct lens details from the camera
viewFrustum.setAspectRatio(aspectRatio);
viewFrustum.setFieldOfView(fov);
viewFrustum.setFieldOfView(fov); // degrees
viewFrustum.setNearClip(nearClip);
viewFrustum.setFarClip(farClip);
viewFrustum.setEyeOffsetPosition(camera.getEyeOffsetPosition());
@ -2191,7 +2169,7 @@ void Application::updateShadowMap() {
glPushMatrix();
glLoadIdentity();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
// store view matrix without translation, which we'll use for precision-sensitive objects
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix);
@ -2270,7 +2248,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
glm::vec3 eyeOffsetPos = whichCamera.getEyeOffsetPosition();
glm::quat eyeOffsetOrient = whichCamera.getEyeOffsetOrientation();
glm::vec3 eyeOffsetAxis = glm::axis(eyeOffsetOrient);
glRotatef(-glm::angle(eyeOffsetOrient), eyeOffsetAxis.x, eyeOffsetAxis.y, eyeOffsetAxis.z);
glRotatef(-glm::degrees(glm::angle(eyeOffsetOrient)), eyeOffsetAxis.x, eyeOffsetAxis.y, eyeOffsetAxis.z);
glTranslatef(-eyeOffsetPos.x, -eyeOffsetPos.y, -eyeOffsetPos.z);
// transform view according to whichCamera
@ -2279,7 +2257,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
glm::quat rotation = whichCamera.getRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(-glm::angle(rotation), axis.x, axis.y, axis.z);
glRotatef(-glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
// store view matrix without translation, which we'll use for precision-sensitive objects
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix);
@ -2483,11 +2461,6 @@ void Application::displayOverlay() {
}
}
// Show chat entry field
if (_chatEntryOn) {
_chatEntry.render(_glWidget->width(), _glWidget->height());
}
// Show on-screen msec timer
if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) {
char frameTimer[10];
@ -2497,7 +2470,7 @@ void Application::displayOverlay() {
(Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth))
? 80 : 20;
drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 1.0f, 0, frameTimer, WHITE_TEXT);
drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 1.0f, 0.f, frameTimer, WHITE_TEXT);
}
_overlays.render2D();
@ -2552,11 +2525,11 @@ void Application::displayStats() {
sprintf(framesPerSecond, "Framerate: %3.0f FPS", _fps);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, serverNodes, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, serverNodes, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarNodes, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarNodes, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, framesPerSecond, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, framesPerSecond, WHITE_TEXT);
if (_statsExpanded) {
char packetsPerSecond[30];
@ -2565,9 +2538,9 @@ void Application::displayStats() {
sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)_bytesPerSecond * 8.f / 1000000.f);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, packetsPerSecond, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, packetsPerSecond, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, averageMegabitsPerSecond, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, averageMegabitsPerSecond, WHITE_TEXT);
}
verticalOffset = 0;
@ -2610,7 +2583,7 @@ void Application::displayStats() {
"Buffer msecs %.1f",
(float) (_audio.getNetworkBufferLengthSamplesPerChannel() + (float) _audio.getJitterBufferSamples()) /
(float)_audio.getNetworkSampleRate() * 1000.f);
drawText(30, _glWidget->height() - 22, 0.10f, 0, 2, audioJitter, WHITE_TEXT);
drawText(30, _glWidget->height() - 22, 0.10f, 0.f, 2.f, audioJitter, WHITE_TEXT);
char audioPing[30];
@ -2623,18 +2596,18 @@ void Application::displayStats() {
sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, audioPing, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, audioPing, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarPing, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPing, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, voxelAvgPing, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelAvgPing, WHITE_TEXT);
if (_statsExpanded) {
char voxelMaxPing[30];
sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, voxelMaxPing, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelMaxPing, WHITE_TEXT);
}
verticalOffset = 0;
@ -2662,11 +2635,11 @@ void Application::displayStats() {
char avatarMixerStats[200];
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarPosition, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPosition, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarVelocity, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarVelocity, WHITE_TEXT);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarBodyYaw, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarBodyYaw, WHITE_TEXT);
if (_statsExpanded) {
SharedNodePointer avatarMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AvatarMixer);
@ -2679,7 +2652,7 @@ void Application::displayStats() {
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, avatarMixerStats, WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, WHITE_TEXT);
}
verticalOffset = 0;
@ -2694,7 +2667,7 @@ void Application::displayStats() {
voxelStats.str("");
voxelStats << "Voxels Memory Nodes: " << VoxelTreeElement::getTotalMemoryUsage() / 1000000.f << "MB";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
voxelStats.str("");
voxelStats <<
@ -2704,14 +2677,14 @@ void Application::displayStats() {
voxelStats << " / GPU: " << _voxels.getVoxelMemoryUsageGPU() / 1000000.f << "MB";
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
// Voxel Rendering
voxelStats.str("");
voxelStats.precision(4);
voxelStats << "Voxel Rendering Slots Max: " << _voxels.getMaxVoxels() / 1000.f << "K";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
}
voxelStats.str("");
@ -2719,7 +2692,7 @@ void Application::displayStats() {
voxelStats << "Drawn: " << _voxels.getVoxelsWritten() / 1000.f << "K " <<
"Abandoned: " << _voxels.getAbandonedVoxels() / 1000.f << "K ";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
// iterate all the current voxel stats, and list their sending modes, and total voxel counts
std::stringstream sendingMode("");
@ -2763,7 +2736,7 @@ void Application::displayStats() {
sendingMode << " <SCENE STABLE>";
}
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)sendingMode.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)sendingMode.str().c_str(), WHITE_TEXT);
}
// Incoming packets
@ -2775,7 +2748,7 @@ void Application::displayStats() {
voxelStats << "Voxel Packets to Process: " << packetsString.toLocal8Bit().constData()
<< " [Recent Max: " << maxString.toLocal8Bit().constData() << "]";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
}
if (_resetRecentMaxPacketsSoon && voxelPacketsToProcess > 0) {
@ -2798,7 +2771,7 @@ void Application::displayStats() {
voxelStats.str("");
voxelStats << "Server voxels: " << serversTotalString.toLocal8Bit().constData();
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
if (_statsExpanded) {
QString serversInternalString = locale.toString((uint)totalInternal);
@ -2809,7 +2782,7 @@ void Application::displayStats() {
"Internal: " << serversInternalString.toLocal8Bit().constData() << " " <<
"Leaves: " << serversLeavesString.toLocal8Bit().constData() << "";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
}
unsigned long localTotal = VoxelTreeElement::getNodeCount();
@ -2819,7 +2792,7 @@ void Application::displayStats() {
voxelStats.str("");
voxelStats << "Local voxels: " << localTotalString.toLocal8Bit().constData();
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
if (_statsExpanded) {
unsigned long localInternal = VoxelTreeElement::getInternalNodeCount();
@ -2832,7 +2805,7 @@ void Application::displayStats() {
"Internal: " << localInternalString.toLocal8Bit().constData() << " " <<
"Leaves: " << localLeavesString.toLocal8Bit().constData() << "";
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, 0.10f, 0, 2, (char*)voxelStats.str().c_str(), WHITE_TEXT);
drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, (char*)voxelStats.str().c_str(), WHITE_TEXT);
}
}
@ -2923,17 +2896,17 @@ glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
void Application::renderRearViewMirror(const QRect& region, bool billboard) {
bool eyeRelativeCamera = false;
if (billboard) {
_mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW);
_mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees
_mirrorCamera.setDistance(BILLBOARD_DISTANCE * _myAvatar->getScale());
_mirrorCamera.setTargetPosition(_myAvatar->getPosition());
} else if (_rearMirrorTools->getZoomLevel() == BODY) {
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW);
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees
_mirrorCamera.setDistance(MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
_mirrorCamera.setTargetPosition(_myAvatar->getChestPosition());
} else { // HEAD zoom level
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW);
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees
_mirrorCamera.setDistance(MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) {
// as a hack until we have a better way of dealing with coordinate precision issues, reposition the
@ -2947,7 +2920,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
}
_mirrorCamera.setAspectRatio((float)region.width() / region.height());
_mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PIf, 0.0f)));
_mirrorCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));
_mirrorCamera.update(1.0f/_fps);
// set the bounds of rear mirror view

View file

@ -64,7 +64,6 @@
#include "renderer/TextureCache.h"
#include "renderer/VoxelShader.h"
#include "ui/BandwidthDialog.h"
#include "ui/ChatEntry.h"
#include "ui/OctreeStatsDialog.h"
#include "ui/RearMirrorTools.h"
#include "ui/LodToolsDialog.h"
@ -95,8 +94,8 @@ static const float NODE_KILLED_BLUE = 0.0f;
static const QString SNAPSHOT_EXTENSION = ".jpg";
static const float BILLBOARD_FIELD_OF_VIEW = 30.0f;
static const float BILLBOARD_DISTANCE = 5.0f;
static const float BILLBOARD_FIELD_OF_VIEW = 30.0f; // degrees
static const float BILLBOARD_DISTANCE = 5.0f; // meters
class Application : public QApplication {
Q_OBJECT
@ -426,9 +425,6 @@ private:
bool _mousePressed; // true if mouse has been pressed (clear when finished)
ChatEntry _chatEntry; // chat entry field
bool _chatEntryOn; // Whether to show the chat entry
GeometryCache _geometryCache;
TextureCache _textureCache;

View file

@ -20,7 +20,6 @@
#include <QtMultimedia/QAudioOutput>
#include <QSvgRenderer>
#include <AngleUtil.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
@ -653,7 +652,7 @@ void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) {
const float MAX_DURATION = 2.f;
const float MIN_AUDIBLE_VOLUME = 0.001f;
const float NOISE_MAGNITUDE = 0.02f;
float frequency = (_drumSoundFrequency / SAMPLE_RATE) * PI_TIMES_TWO;
float frequency = (_drumSoundFrequency / SAMPLE_RATE) * TWO_PI;
if (_drumSoundVolume > 0.f) {
for (int i = 0; i < numSamples; i++) {
t = (float) _drumSoundSample + (float) i;

View file

@ -81,7 +81,7 @@ void Camera::updateFollowMode(float deltaTime) {
_distance = _newDistance;
_tightness = _newTightness;
} else {
_modeShift = ONE_HALF - ONE_HALF * cosf(_linearModeShift * PIE );
_modeShift = ONE_HALF - ONE_HALF * cosf(_linearModeShift * PI );
_upShift = _previousUpShift * (1.0f - _modeShift) + _newUpShift * _modeShift;
_distance = _previousDistance * (1.0f - _modeShift) + _newDistance * _modeShift;
_tightness = _previousTightness * (1.0f - _modeShift) + _newTightness * _modeShift;

View file

@ -89,7 +89,7 @@ private:
glm::vec3 _position;
glm::vec3 _idealPosition;
glm::vec3 _targetPosition;
float _fieldOfView;
float _fieldOfView; // degrees
float _aspectRatio;
float _nearClip;
float _farClip;

View file

@ -237,8 +237,8 @@ void Environment::renderAtmosphere(Camera& camera, const EnvironmentData& data)
program->setUniformValue(locations[INNER_RADIUS_LOCATION], data.getAtmosphereInnerRadius());
program->setUniformValue(locations[KR_ESUN_LOCATION], data.getRayleighScattering() * data.getSunBrightness());
program->setUniformValue(locations[KM_ESUN_LOCATION], data.getMieScattering() * data.getSunBrightness());
program->setUniformValue(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PIf);
program->setUniformValue(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PIf);
program->setUniformValue(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PI);
program->setUniformValue(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PI);
program->setUniformValue(locations[SCALE_LOCATION], 1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius()));
program->setUniformValue(locations[SCALE_DEPTH_LOCATION], 0.25f);
program->setUniformValue(locations[SCALE_OVER_SCALE_DEPTH_LOCATION],

View file

@ -170,7 +170,11 @@ Menu::Menu() :
addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor()));
addActionToQMenuAndActionHash(toolsMenu, MenuOption::FstUploader, 0, Application::getInstance(), SLOT(uploadFST()));
_chatAction = addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, 0, this, SLOT(showChat()));
_chatAction = addActionToQMenuAndActionHash(toolsMenu,
MenuOption::Chat,
Qt::Key_Return,
this,
SLOT(showChat()));
#ifdef HAVE_QXMPP
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
toggleChat();
@ -717,8 +721,8 @@ void Menu::editPreferences() {
form->addRow("Faceshift Eye Deflection:", faceshiftEyeDeflection);
QSpinBox* fieldOfView = new QSpinBox();
fieldOfView->setMaximum(180);
fieldOfView->setMinimum(1);
fieldOfView->setMaximum(180.f);
fieldOfView->setMinimum(1.f);
fieldOfView->setValue(_fieldOfView);
form->addRow("Vertical Field of View (Degrees):", fieldOfView);

View file

@ -9,6 +9,8 @@
#include <QMutexLocker>
#include <QtDebug>
#include <glm/gtx/transform.hpp>
#include <SharedUtil.h>
#include <MetavoxelUtil.h>
@ -18,6 +20,7 @@
#include "MetavoxelSystem.h"
#include "renderer/Model.h"
REGISTER_META_OBJECT(SphereRenderer)
REGISTER_META_OBJECT(StaticModelRenderer)
ProgramObject MetavoxelSystem::_program;
@ -41,6 +44,31 @@ void MetavoxelSystem::init() {
connect(NodeList::getInstance(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachClient(const SharedNodePointer&)));
}
SharedObjectPointer MetavoxelSystem::findFirstRaySpannerIntersection(
const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance) {
SharedObjectPointer closestSpanner;
float closestDistance = FLT_MAX;
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex());
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) {
float clientDistance;
SharedObjectPointer clientSpanner = client->getData().findFirstRaySpannerIntersection(
origin, direction, attribute, clientDistance);
if (clientSpanner && clientDistance < closestDistance) {
closestSpanner = clientSpanner;
closestDistance = clientDistance;
}
}
}
}
if (closestSpanner) {
distance = closestDistance;
}
return closestSpanner;
}
void MetavoxelSystem::applyEdit(const MetavoxelEditMessage& edit) {
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getType() == NodeType::MetavoxelServer) {
@ -57,13 +85,14 @@ void MetavoxelSystem::simulate(float deltaTime) {
// simulate the clients
_points.clear();
_simulateVisitor.setDeltaTime(deltaTime);
_simulateVisitor.setOrder(-Application::getInstance()->getViewFrustum()->getDirection());
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex());
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) {
client->simulate(deltaTime);
client->getData().guide(_simulateVisitor);
client->guide(_simulateVisitor);
}
}
}
@ -123,7 +152,7 @@ void MetavoxelSystem::render() {
QMutexLocker locker(&node->getMutex());
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) {
client->getData().guide(_renderVisitor);
client->guide(_renderVisitor);
}
}
}
@ -143,15 +172,16 @@ MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector<Point>& points) :
_points(points) {
}
void MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner) {
bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner) {
spanner->getRenderer()->simulate(_deltaTime);
return true;
}
bool MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) {
int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) {
SpannerVisitor::visit(info);
if (!info.isLeaf) {
return true;
return _order;
}
QRgb color = info.inputValues.at(0).getInlineValue<QRgb>();
QRgb normal = info.inputValues.at(1).getInlineValue<QRgb>();
@ -161,15 +191,16 @@ bool MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) {
{ qRed(color), qGreen(color), qBlue(color), alpha }, { qRed(normal), qGreen(normal), qBlue(normal) } };
_points.append(point);
}
return false;
return STOP_RECURSION;
}
MetavoxelSystem::RenderVisitor::RenderVisitor() :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute()) {
}
void MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) {
bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) {
spanner->getRenderer()->render(1.0f);
return true;
}
MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) :
@ -178,11 +209,16 @@ MetavoxelClient::MetavoxelClient(const SharedNodePointer& node) :
connect(&_sequencer, SIGNAL(readyToWrite(const QByteArray&)), SLOT(sendData(const QByteArray&)));
connect(&_sequencer, SIGNAL(readyToRead(Bitstream&)), SLOT(readPacket(Bitstream&)));
connect(&_sequencer, SIGNAL(sendAcknowledged(int)), SLOT(clearSendRecordsBefore(int)));
connect(&_sequencer, SIGNAL(receiveAcknowledged(int)), SLOT(clearReceiveRecordsBefore(int)));
// insert the baseline send record
SendRecord sendRecord = { 0 };
_sendRecords.append(sendRecord);
// insert the baseline receive record
ReceiveRecord record = { 0, _data };
_receiveRecords.append(record);
ReceiveRecord receiveRecord = { 0, _data };
_receiveRecords.append(receiveRecord);
}
MetavoxelClient::~MetavoxelClient() {
@ -192,9 +228,19 @@ MetavoxelClient::~MetavoxelClient() {
_sequencer.endPacket();
}
static MetavoxelLOD getLOD() {
const float FIXED_LOD_THRESHOLD = 0.01f;
return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD);
}
void MetavoxelClient::guide(MetavoxelVisitor& visitor) {
visitor.setLOD(getLOD());
_data.guide(visitor);
}
void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) {
// apply immediately to local tree
edit.apply(_data);
edit.apply(_data, _sequencer.getWeakSharedObjectHash());
// start sending it out
_sequencer.sendHighPriorityMessage(QVariant::fromValue(edit));
@ -202,9 +248,14 @@ void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit) {
void MetavoxelClient::simulate(float deltaTime) {
Bitstream& out = _sequencer.startPacket();
ClientStateMessage state = { Application::getInstance()->getCamera()->getPosition() };
ClientStateMessage state = { getLOD() };
out << QVariant::fromValue(state);
_sequencer.endPacket();
// record the send
SendRecord record = { _sequencer.getOutgoingPacketNumber(), state.lod };
_sendRecords.append(record);
}
int MetavoxelClient::parseData(const QByteArray& packet) {
@ -223,17 +274,21 @@ void MetavoxelClient::readPacket(Bitstream& in) {
handleMessage(message, in);
// record the receipt
ReceiveRecord record = { _sequencer.getIncomingPacketNumber(), _data };
ReceiveRecord record = { _sequencer.getIncomingPacketNumber(), _data, _sendRecords.first().lod };
_receiveRecords.append(record);
// reapply local edits
foreach (const DatagramSequencer::HighPriorityMessage& message, _sequencer.getHighPriorityMessages()) {
if (message.data.userType() == MetavoxelEditMessage::Type) {
message.data.value<MetavoxelEditMessage>().apply(_data);
message.data.value<MetavoxelEditMessage>().apply(_data, _sequencer.getWeakSharedObjectHash());
}
}
}
void MetavoxelClient::clearSendRecordsBefore(int index) {
_sendRecords.erase(_sendRecords.begin(), _sendRecords.begin() + index + 1);
}
void MetavoxelClient::clearReceiveRecordsBefore(int index) {
_receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1);
}
@ -241,7 +296,7 @@ void MetavoxelClient::clearReceiveRecordsBefore(int index) {
void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) {
int userType = message.userType();
if (userType == MetavoxelDeltaMessage::Type) {
_data.readDelta(_receiveRecords.first().data, in);
_data.readDelta(_receiveRecords.first().data, _receiveRecords.first().lod, in, _sendRecords.first().lod);
} else if (userType == QMetaType::QVariantList) {
foreach (const QVariant& element, message.toList()) {
@ -250,6 +305,26 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) {
}
}
SphereRenderer::SphereRenderer() {
}
void SphereRenderer::render(float alpha) {
Sphere* sphere = static_cast<Sphere*>(parent());
const QColor& color = sphere->getColor();
glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha);
glPushMatrix();
const glm::vec3& translation = sphere->getTranslation();
glTranslatef(translation.x, translation.y, translation.z);
glm::quat rotation = glm::quat(glm::radians(sphere->getRotation()));
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
glutSolidSphere(sphere->getScale(), 10, 10);
glPopMatrix();
}
StaticModelRenderer::StaticModelRenderer() :
_model(new Model(this)) {
}
@ -270,6 +345,14 @@ void StaticModelRenderer::init(Spanner* spanner) {
}
void StaticModelRenderer::simulate(float deltaTime) {
// update the bounds
Box bounds;
if (_model->isActive()) {
const Extents& extents = _model->getGeometry()->getFBXGeometry().meshExtents;
bounds = Box(extents.minimum, extents.maximum);
}
static_cast<StaticModel*>(parent())->setBounds(glm::translate(_model->getTranslation()) *
glm::mat4_cast(_model->getRotation()) * glm::scale(_model->getScale()) * bounds);
_model->simulate(deltaTime);
}
@ -277,6 +360,10 @@ void StaticModelRenderer::render(float alpha) {
_model->render(alpha);
}
bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
return _model->findRayIntersection(origin, direction, distance);
}
void StaticModelRenderer::applyTranslation(const glm::vec3& translation) {
_model->setTranslation(translation);
}

View file

@ -35,6 +35,9 @@ public:
void init();
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
const AttributePointer& attribute, float& distance);
void applyEdit(const MetavoxelEditMessage& edit);
void simulate(float deltaTime);
@ -57,18 +60,20 @@ private:
public:
SimulateVisitor(QVector<Point>& points);
void setDeltaTime(float deltaTime) { _deltaTime = deltaTime; }
virtual void visit(Spanner* spanner);
virtual bool visit(MetavoxelInfo& info);
void setOrder(const glm::vec3& direction) { _order = encodeOrder(direction); }
virtual bool visit(Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
QVector<Point>& _points;
float _deltaTime;
int _order;
};
class RenderVisitor : public SpannerVisitor {
public:
RenderVisitor();
virtual void visit(Spanner* spanner);
virtual bool visit(Spanner* spanner);
};
static ProgramObject _program;
@ -91,6 +96,8 @@ public:
MetavoxelData& getData() { return _data; }
void guide(MetavoxelVisitor& visitor);
void applyEdit(const MetavoxelEditMessage& edit);
void simulate(float deltaTime);
@ -103,16 +110,25 @@ private slots:
void readPacket(Bitstream& in);
void clearSendRecordsBefore(int index);
void clearReceiveRecordsBefore(int index);
private:
void handleMessage(const QVariant& message, Bitstream& in);
class SendRecord {
public:
int packetNumber;
MetavoxelLOD lod;
};
class ReceiveRecord {
public:
int packetNumber;
MetavoxelData data;
MetavoxelLOD lod;
};
SharedNodePointer _node;
@ -121,9 +137,21 @@ private:
MetavoxelData _data;
QList<SendRecord> _sendRecords;
QList<ReceiveRecord> _receiveRecords;
};
/// Renders spheres.
class SphereRenderer : public SpannerRenderer {
Q_OBJECT
public:
Q_INVOKABLE SphereRenderer();
virtual void render(float alpha);
};
/// Renders static models.
class StaticModelRenderer : public SpannerRenderer {
Q_OBJECT
@ -135,11 +163,12 @@ public:
virtual void init(Spanner* spanner);
virtual void simulate(float deltaTime);
virtual void render(float alpha);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
private slots:
void applyTranslation(const glm::vec3& translation);
void applyRotation(const glm::vec3& rotation);
void applyRotation(const glm::vec3& eulerAngles); // eulerAngles are in degrees
void applyScale(float scale);
void applyURL(const QUrl& url);

View file

@ -30,9 +30,8 @@ bool Stars::setResolution(unsigned k) {
}
void Stars::render(float fovY, float aspect, float nearZ, float alpha) {
// determine length of screen diagonal from quadrant height and aspect ratio
float quadrantHeight = nearZ * tan(angleConvert<Degrees,Radians>(fovY) * 0.5f);
float quadrantHeight = nearZ * tan(RADIANS_PER_DEGREE * fovY * 0.5f);
float halfDiagonal = sqrt(quadrantHeight * quadrantHeight * (1.0f + aspect * aspect));
// determine fov angle in respect to the diagonal

View file

@ -68,19 +68,21 @@ void printVector(glm::vec3 vec) {
printf("%4.2f, %4.2f, %4.2f\n", vec.x, vec.y, vec.z);
}
// Return the azimuth angle in degrees between two points.
// Return the azimuth angle (in radians) between two points.
float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos) {
return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) * 180.0f / PIf;
return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z);
}
// Return the angle in degrees between the head and an object in the scene. The value is zero if you are looking right at it. The angle is negative if the object is to your right.
// Return the angle (in radians) between the head and an object in the scene.
// The value is zero if you are looking right at it.
// The angle is negative if the object is to your right.
float angle_to(glm::vec3 head_pos, glm::vec3 source_pos, float render_yaw, float head_yaw) {
return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) * 180.0f / PIf + render_yaw + head_yaw;
return atan2(head_pos.x - source_pos.x, head_pos.z - source_pos.z) + render_yaw + head_yaw;
}
// Helper function returns the positive angle in degrees between two 3D vectors
// Helper function returns the positive angle (in radians) between two 3D vectors
float angleBetween(const glm::vec3& v1, const glm::vec3& v2) {
return acos((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2))) * 180.f / PIf;
return acosf((glm::dot(v1, v2)) / (glm::length(v1) * glm::length(v2)));
}
// Helper function return the rotation from the first vector onto the second
@ -90,7 +92,7 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
return glm::quat();
}
glm::vec3 axis;
if (angle > 179.99f) { // 180 degree rotation; must use another axis
if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis
axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f));
float axisLength = glm::length(axis);
if (axisLength < EPSILON) { // parallel to x; y will work
@ -314,7 +316,7 @@ float widthChar(float scale, int mono, char ch) {
return textRenderer(mono)->computeWidth(ch) * (scale / 0.10);
}
void drawText(int x, int y, float scale, float rotate, int mono,
void drawText(int x, int y, float scale, float radians, int mono,
char const* string, const float* color) {
//
// Draws text on screen as stroked so it can be resized
@ -322,16 +324,16 @@ void drawText(int x, int y, float scale, float rotate, int mono,
glPushMatrix();
glTranslatef(static_cast<float>(x), static_cast<float>(y), 0.0f);
glColor3fv(color);
glRotated(rotate,0,0,1);
glScalef(scale / 0.10, scale / 0.10, 1.0);
glRotated(double(radians * DEGREES_PER_RADIAN), 0.0, 0.0, 1.0);
glScalef(scale / 0.1f, scale / 0.1f, 1.f);
textRenderer(mono)->draw(0, 0, string);
glPopMatrix();
}
void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec, float r, float g, float b) {
void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, float r, float g, float b) {
//
// Draws text on screen as stroked so it can be resized
// Draws vec3 on screen as stroked so it can be resized
//
char vectext[20];
sprintf(vectext,"%3.1f,%3.1f,%3.1f", vec.x, vec.y, vec.z);
@ -339,10 +341,10 @@ void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, gl
glPushMatrix();
glTranslatef(static_cast<float>(x), static_cast<float>(y), 0);
glColor3f(r,g,b);
glRotated(180+rotate,0,0,1);
glRotated(180,0,1,0);
glRotated(180.0 + double(radians * DEGREES_PER_RADIAN), 0.0, 0.0, 1.0);
glRotated(180.0, 0.0, 1.0, 0.0);
glLineWidth(thick);
glScalef(scale, scale, 1.0);
glScalef(scale, scale, 1.f);
len = (int) strlen(vectext);
for (i = 0; i < len; i++) {
if (!mono) glutStrokeCharacter(GLUT_STROKE_ROMAN, int(vectext[i]));
@ -371,9 +373,9 @@ void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::ve
glBegin(GL_LINE_STRIP);
for (int i=0; i<numSides+1; i++) {
float r = ((float)i / (float)numSides) * PIf * 2.0;
float s = radius * sin(r);
float c = radius * cos(r);
float r = ((float)i / (float)numSides) * TWO_PI;
float s = radius * sinf(r);
float c = radius * cosf(r);
glVertex3f
(
@ -394,9 +396,9 @@ void renderCircle(glm::vec3 position, float radius, glm::vec3 surfaceNormal, int
glBegin(GL_LINE_STRIP);
for (int i=0; i<numSides+1; i++) {
float r = ((float)i / (float)numSides) * PIf * 2.0;
float s = radius * sin(r);
float c = radius * cos(r);
float r = ((float)i / (float)numSides) * TWO_PI;
float s = radius * sinf(r);
float c = radius * cosf(r);
glVertex3f
(
position.x + perp1.x * s + perp2.x * c,
@ -440,12 +442,12 @@ void renderRoundedCornersRect(int x, int y, int width, int height, int radius, i
numPointsCorner = MAX_POINTS_CORNER;
}
// Precompute sin and cos for [0, pi/2) for the number of points (numPointCorner)
// Precompute sin and cos for [0, PI/2) for the number of points (numPointCorner)
double radiusTimesSin[MAX_POINTS_CORNER];
double radiusTimesCos[MAX_POINTS_CORNER];
int i = 0;
for (int i = 0; i < numPointsCorner; i++) {
double t = i * PIf / (2.0f * (numPointsCorner - 1));
double t = (double)i * (double)PI_OVER_TWO / (double)(numPointsCorner - 1);
radiusTimesSin[i] = radius * sin(t);
radiusTimesCos[i] = radius * cos(t);
}

View file

@ -31,10 +31,10 @@ void renderWorldBox();
int widthText(float scale, int mono, char const* string);
float widthChar(float scale, int mono, char ch);
void drawText(int x, int y, float scale, float rotate, int mono,
void drawText(int x, int y, float scale, float radians, int mono,
char const* string, const float* color);
void drawvec3(int x, int y, float scale, float rotate, float thick, int mono, glm::vec3 vec,
void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec,
float r=1.0, float g=1.0, float b=1.0);
void drawVector(glm::vec3* vector);

View file

@ -207,8 +207,8 @@ void Avatar::render(bool forShadowMap) {
}
// render voice intensity sphere for avatars that are farther away
const float MAX_SPHERE_ANGLE = 10.f;
const float MIN_SPHERE_ANGLE = 1.f;
const float MAX_SPHERE_ANGLE = 10.f * RADIANS_PER_DEGREE;
const float MIN_SPHERE_ANGLE = 1.f * RADIANS_PER_DEGREE;
const float MIN_SPHERE_SIZE = 0.01f;
const float SPHERE_LOUDNESS_SCALING = 0.0005f;
const float SPHERE_COLOR[] = { 0.5f, 0.8f, 0.8f };
@ -245,12 +245,12 @@ void Avatar::render(bool forShadowMap) {
glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z);
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
glm::vec3 chatAxis = glm::axis(chatRotation);
glRotatef(glm::angle(chatRotation), chatAxis.x, chatAxis.y, chatAxis.z);
glRotatef(glm::degrees(glm::angle(chatRotation)), chatAxis.x, chatAxis.y, chatAxis.z);
glColor3f(0, 0.8f, 0);
glRotatef(180, 0, 1, 0);
glRotatef(180, 0, 0, 1);
glColor3f(0.f, 0.8f, 0.f);
glRotatef(180.f, 0.f, 1.f, 0.f);
glRotatef(180.f, 0.f, 0.f, 1.f);
glScalef(_scale * CHAT_MESSAGE_SCALE, _scale * CHAT_MESSAGE_SCALE, 1.0f);
glDisable(GL_LIGHTING);
@ -266,7 +266,7 @@ void Avatar::render(bool forShadowMap) {
_chatMessage[lastIndex] = '\0';
textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str());
_chatMessage[lastIndex] = lastChar;
glColor3f(0, 1, 0);
glColor3f(0.f, 1.f, 0.f);
textRenderer(CHAT)->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex);
}
glEnable(GL_LIGHTING);
@ -279,12 +279,12 @@ void Avatar::render(bool forShadowMap) {
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
glm::quat orientation = getOrientation();
glm::vec3 currentUp = orientation * IDENTITY_UP;
float angle = glm::degrees(acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f)));
float angle = acosf(glm::clamp(glm::dot(currentUp, _worldUpDirection), -1.0f, 1.0f));
if (angle < EPSILON) {
return glm::quat();
}
glm::vec3 axis;
if (angle > 179.99f) { // 180 degree rotation; must use another axis
if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis
axis = orientation * IDENTITY_RIGHT;
} else {
axis = glm::normalize(glm::cross(currentUp, _worldUpDirection));
@ -334,9 +334,9 @@ void Avatar::renderBillboard() {
// rotate about vertical to face the camera
glm::quat rotation = getOrientation();
glm::vec3 cameraVector = glm::inverse(rotation) * (Application::getInstance()->getCamera()->getPosition() - _position);
rotation = rotation * glm::angleAxis(glm::degrees(atan2f(-cameraVector.x, -cameraVector.z)), glm::vec3(0.0f, 1.0f, 0.0f));
rotation = rotation * glm::angleAxis(atan2f(-cameraVector.x, -cameraVector.z), glm::vec3(0.0f, 1.0f, 0.0f));
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
// compute the size from the billboard camera parameters and scale
float size = _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
@ -382,7 +382,7 @@ void Avatar::renderDisplayName() {
// we need "always facing camera": we must remove the camera rotation from the stack
glm::quat rotation = Application::getInstance()->getCamera()->getRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
// We need to compute the scale factor such as the text remains with fixed size respect to window coordinates
// We project a unit vector and check the difference in screen coordinates, to check which is the
@ -640,15 +640,15 @@ void Avatar::renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2,
glm::vec3 perpCos = glm::normalize(glm::cross(axis, perpSin));
perpSin = glm::cross(perpCos, axis);
float anglea = 0.0;
float angleb = 0.0;
float anglea = 0.f;
float angleb = 0.f;
for (int i = 0; i < NUM_BODY_CONE_SIDES; i ++) {
// the rectangles that comprise the sides of the cone section are
// referenced by "a" and "b" in one dimension, and "1", and "2" in the other dimension.
anglea = angleb;
angleb = ((float)(i+1) / (float)NUM_BODY_CONE_SIDES) * PIf * 2.0f;
angleb = ((float)(i+1) / (float)NUM_BODY_CONE_SIDES) * TWO_PI;
float sa = sinf(anglea);
float sb = sinf(angleb);

View file

@ -54,6 +54,9 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
++avatarIterator;
continue;
}
if (!avatar->isInitialized()) {
avatar->init();
}
if (avatar->getOwningAvatarMixer()) {
// this avatar's mixer is still around, go ahead and simulate it
avatar->simulate(deltaTime);
@ -80,7 +83,7 @@ void AvatarManager::renderAvatars(bool forShadowMapOrMirror, bool selfAvatarOnly
foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) {
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
if (!avatar->isInitialized()) {
avatar->init();
continue;
}
if (avatar == static_cast<Avatar*>(_myAvatar.data())) {
_myAvatar->render(forShadowMapOrMirror);

View file

@ -56,9 +56,10 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
glm::mat3 axes = glm::mat3_cast(_rotation);
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) *
joint.preTransform * glm::mat4_cast(joint.preRotation)));
state.rotation = glm::angleAxis(-_owningHead->getTweakedRoll(), glm::normalize(inverse * axes[2])) *
glm::angleAxis(_owningHead->getTweakedYaw(), glm::normalize(inverse * axes[1])) *
glm::angleAxis(-_owningHead->getTweakedPitch(), glm::normalize(inverse * axes[0])) * joint.rotation;
state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getTweakedRoll(), glm::normalize(inverse * axes[2]))
* glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getTweakedYaw(), glm::normalize(inverse * axes[1]))
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getTweakedPitch(), glm::normalize(inverse * axes[0]))
* joint.rotation;
}
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
@ -69,7 +70,7 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() +
_owningHead->getSaccade() - _translation, 1.0f));
glm::quat between = rotationBetween(front, lookAt);
const float MAX_ANGLE = 30.0f;
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
state.rotation = glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
joint.rotation;
}

View file

@ -183,13 +183,13 @@ void Head::setScale (float scale) {
}
glm::quat Head::getTweakedOrientation() const {
return _owningAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(getTweakedPitch(), getTweakedYaw(), getTweakedRoll() )));
return _owningAvatar->getOrientation() * glm::quat(glm::radians(
glm::vec3(getTweakedPitch(), getTweakedYaw(), getTweakedRoll() )));
}
glm::quat Head::getCameraOrientation () const {
Avatar* owningAvatar = static_cast<Avatar*>(_owningAvatar);
return owningAvatar->getWorldAlignedOrientation()
* glm::quat(glm::radians(glm::vec3(_pitch, 0.f, 0.0f)));
return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_pitch, 0.f, 0.0f)));
}
glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const {

View file

@ -34,8 +34,8 @@
using namespace std;
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
const float YAW_MAG = 500.0f;
const float PITCH_MAG = 100.0f;
const float YAW_SPEED = 500.0f; // degrees/sec
const float PITCH_SPEED = 100.0f; // degrees/sec
const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions
const float COLLISION_RADIUS_SCALE = 0.125f;
@ -113,12 +113,13 @@ void MyAvatar::update(float deltaTime) {
Head* head = getHead();
if (OculusManager::isConnected()) {
float yaw, pitch, roll;
float yaw, pitch, roll; // these angles will be in radians
OculusManager::getEulerAngles(yaw, pitch, roll);
head->setYaw(yaw);
head->setPitch(pitch);
head->setRoll(roll);
// but these euler angles are stored in degrees
head->setYaw(yaw * DEGREES_PER_RADIAN);
head->setPitch(pitch * DEGREES_PER_RADIAN);
head->setRoll(roll * DEGREES_PER_RADIAN);
}
// Get audio loudness data from audio input device
@ -181,7 +182,7 @@ void MyAvatar::simulate(float deltaTime) {
float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE;
if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) {
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f));
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cosf(0.5f * RADIANS_PER_DEGREE * myCamera->getFieldOfView()));
radius *= COLLISION_RADIUS_SCALAR;
}
@ -205,7 +206,7 @@ void MyAvatar::simulate(float deltaTime) {
// decay body rotation momentum
const float BODY_SPIN_FRICTION = 7.5f;
float bodySpinMomentum = 1.0 - BODY_SPIN_FRICTION * deltaTime;
float bodySpinMomentum = 1.f - BODY_SPIN_FRICTION * deltaTime;
if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; }
_bodyPitchDelta *= bodySpinMomentum;
_bodyYawDelta *= bodySpinMomentum;
@ -333,12 +334,12 @@ void MyAvatar::updateFromGyros(float deltaTime) {
bool trackerActive = false;
if (faceshift->isActive()) {
estimatedPosition = faceshift->getHeadTranslation();
estimatedRotation = safeEulerAngles(faceshift->getHeadRotation());
estimatedRotation = glm::degrees(safeEulerAngles(faceshift->getHeadRotation()));
trackerActive = true;
} else if (visage->isActive()) {
estimatedPosition = visage->getHeadTranslation();
estimatedRotation = safeEulerAngles(visage->getHeadRotation());
estimatedRotation = glm::degrees(safeEulerAngles(visage->getHeadRotation()));
trackerActive = true;
}
@ -485,12 +486,11 @@ void MyAvatar::render(bool forShadowMapOrMirror) {
glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z);
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
glm::vec3 chatAxis = glm::axis(chatRotation);
glRotatef(glm::angle(chatRotation), chatAxis.x, chatAxis.y, chatAxis.z);
glRotatef(glm::degrees(glm::angle(chatRotation)), chatAxis.x, chatAxis.y, chatAxis.z);
glColor3f(0, 0.8f, 0);
glRotatef(180, 0, 1, 0);
glRotatef(180, 0, 0, 1);
glColor3f(0.f, 0.8f, 0.f);
glRotatef(180.f, 0.f, 1.f, 0.f);
glRotatef(180.f, 0.f, 0.f, 1.f);
glScalef(_scale * CHAT_MESSAGE_SCALE, _scale * CHAT_MESSAGE_SCALE, 1.0f);
glDisable(GL_LIGHTING);
@ -506,7 +506,7 @@ void MyAvatar::render(bool forShadowMapOrMirror) {
_chatMessage[lastIndex] = '\0';
textRenderer()->draw(-width / 2.0f, 0, _chatMessage.c_str());
_chatMessage[lastIndex] = lastChar;
glColor3f(0, 1, 0);
glColor3f(0.f, 1.f, 0.f);
textRenderer()->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex);
}
glEnable(GL_LIGHTING);
@ -520,7 +520,7 @@ void MyAvatar::renderHeadMouse() const {
// TODO? resurrect headMouse stuff?
/*
// Display small target box at center or head mouse target that can also be used to measure LOD
glColor3f(1.0, 1.0, 1.0);
glColor3f(1.f, 1.f, 1.f);
glDisable(GL_LINE_SMOOTH);
const int PIXEL_BOX = 16;
glBegin(GL_LINES);
@ -542,7 +542,7 @@ void MyAvatar::renderHeadMouse() const {
int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE;
int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE;
glColor3f(0.0, 1.0, 1.0);
glColor3f(0.f, 1.f, 1.f);
glDisable(GL_LINE_SMOOTH);
glBegin(GL_LINES);
glVertex2f(eyeTargetX - PIXEL_BOX/2, eyeTargetY);
@ -617,15 +617,15 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
// first orbit horizontally
glm::quat orientation = getOrientation();
const float ANGULAR_SCALE = 0.5f;
glm::quat rotation = glm::angleAxis(deltaX * -ANGULAR_SCALE, orientation * IDENTITY_UP);
glm::quat rotation = glm::angleAxis(glm::radians(- deltaX * ANGULAR_SCALE), orientation * IDENTITY_UP);
setPosition(position + rotation * (getPosition() - position));
orientation = rotation * orientation;
setOrientation(orientation);
// then vertically
float oldPitch = getHead()->getPitch();
getHead()->setPitch(oldPitch + deltaY * -ANGULAR_SCALE);
rotation = glm::angleAxis(getHead()->getPitch() - oldPitch, orientation * IDENTITY_RIGHT);
getHead()->setPitch(oldPitch - deltaY * ANGULAR_SCALE);
rotation = glm::angleAxis(glm::radians((getHead()->getPitch() - oldPitch)), orientation * IDENTITY_RIGHT);
setPosition(position + rotation * (getPosition() - position));
}
@ -730,9 +730,9 @@ void MyAvatar::updateThrust(float deltaTime) {
_thrust -= _driveKeys[LEFT] * _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right;
_thrust += _driveKeys[UP] * _scale * THRUST_MAG_UP * _thrustMultiplier * deltaTime * up;
_thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_MAG * deltaTime;
_bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_MAG * deltaTime;
getHead()->setPitch(getHead()->getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime);
_bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime;
_bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime;
getHead()->setPitch(getHead()->getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime);
// If thrust keys are being held down, slowly increase thrust to allow reaching great speeds
if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {
@ -1030,7 +1030,7 @@ void MyAvatar::updateChatCircle(float deltaTime) {
const float CIRCLE_INFLUENCE_SCALE = 2.0f;
const float MIN_RADIUS = 0.3f;
for (int i = sortedAvatars.size() - 1; i >= 0; i--) {
float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / PI_TIMES_TWO);
float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (i + 2)) / TWO_PI);
if (glm::distance(_position, sortedAvatars[i].accumulatedCenter) > radius * CIRCLE_INFLUENCE_SCALE) {
sortedAvatars.remove(i);
} else {
@ -1041,7 +1041,7 @@ void MyAvatar::updateChatCircle(float deltaTime) {
return;
}
center = sortedAvatars.last().accumulatedCenter;
float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / PI_TIMES_TWO);
float radius = qMax(MIN_RADIUS, (CIRCUMFERENCE_PER_MEMBER * (sortedAvatars.size() + 1)) / TWO_PI);
// compute the average up vector
glm::vec3 up = getWorldAlignedOrientation() * IDENTITY_UP;
@ -1062,18 +1062,18 @@ void MyAvatar::updateChatCircle(float deltaTime) {
glm::vec3 delta = _position - center;
glm::vec3 projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f);
float myAngle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f;
float leftDistance = PI_TIMES_TWO;
float rightDistance = PI_TIMES_TWO;
float leftDistance = TWO_PI;
float rightDistance = TWO_PI;
foreach (const SortedAvatar& sortedAvatar, sortedAvatars) {
delta = sortedAvatar.avatar->getPosition() - center;
projected = glm::vec3(glm::dot(right, delta), glm::dot(front, delta), 0.0f);
float angle = glm::length(projected) > EPSILON ? atan2f(projected.y, projected.x) : 0.0f;
if (angle < myAngle) {
leftDistance = min(myAngle - angle, leftDistance);
rightDistance = min(PI_TIMES_TWO - (myAngle - angle), rightDistance);
rightDistance = min(TWO_PI - (myAngle - angle), rightDistance);
} else {
leftDistance = min(PI_TIMES_TWO - (angle - myAngle), leftDistance);
leftDistance = min(TWO_PI - (angle - myAngle), leftDistance);
rightDistance = min(angle - myAngle, rightDistance);
}
}
@ -1119,13 +1119,6 @@ void MyAvatar::setGravity(glm::vec3 gravity) {
}
}
void MyAvatar::setOrientation(const glm::quat& orientation) {
glm::vec3 eulerAngles = safeEulerAngles(orientation);
_bodyPitch = eulerAngles.x;
_bodyYaw = eulerAngles.y;
_bodyRoll = eulerAngles.z;
}
void MyAvatar::goHome() {
qDebug("Going Home!");
setPosition(START_LOCATION);
@ -1162,7 +1155,7 @@ void MyAvatar::updateLocationInDataServer() {
if (accountManager.isLoggedIn()) {
QString positionString(createByteArray(_position));
QString orientationString(createByteArray(safeEulerAngles(getOrientation())));
QString orientationString(createByteArray(glm::degrees(safeEulerAngles(getOrientation()))));
// construct the json to put the user's location
QString locationPutJson = QString() + "{\"address\":{\"position\":\""
@ -1195,10 +1188,10 @@ void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
NodeList::getInstance()->getDomainInfo().setHostname(domainHostnameString);
// orient the user to face the target
glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(),
glm::quat newOrientation = glm::quat(glm::vec3(orientationItems[0].toFloat(),
orientationItems[1].toFloat(),
orientationItems[2].toFloat())))
* glm::angleAxis(180.0f, glm::vec3(0.0f, 1.0f, 0.0f));
orientationItems[2].toFloat()))
* glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
setOrientation(newOrientation);
// move the user a couple units away

View file

@ -44,18 +44,16 @@ public:
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
void setLeanScale(float scale) { _leanScale = scale; }
void setGravity(glm::vec3 gravity);
void setOrientation(const glm::quat& orientation);
void setMoveTarget(const glm::vec3 moveTarget);
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
// getters
float getSpeed() const { return _speed; }
AvatarMode getMode() const { return _mode; }
float getLeanScale() const { return _leanScale; }
float getElapsedTimeStopped() const { return _elapsedTimeStopped; }
float getElapsedTimeMoving() const { return _elapsedTimeMoving; }
float getAbsoluteHeadYaw() const;
float getAbsoluteHeadYaw() const; // degrees
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
glm::vec3 getGravity() const { return _gravity; }
@ -68,7 +66,7 @@ public:
// Set what driving keys are being pressed to control thrust levels
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
bool getDriveKeys(int key) { return _driveKeys[key]; };
bool getDriveKeys(int key) { return _driveKeys[key] != 0.f; };
void jump() { _shouldJump = true; };
bool isMyAvatar() { return true; }
@ -101,8 +99,8 @@ public slots:
private:
bool _mousePressed;
float _bodyPitchDelta;
float _bodyRollDelta;
float _bodyPitchDelta; // degrees
float _bodyRollDelta; // degrees
bool _shouldJump;
float _driveKeys[MAX_DRIVE_KEYS];
glm::vec3 _gravity;

View file

@ -19,18 +19,14 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
}
void SkeletonModel::simulate(float deltaTime, bool delayLoad) {
if (!isActive()) {
Model::simulate(deltaTime, delayLoad);
return;
}
setTranslation(_owningAvatar->getPosition());
setRotation(_owningAvatar->getOrientation() * glm::angleAxis(180.0f, glm::vec3(0.0f, 1.0f, 0.0f)));
setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)));
const float MODEL_SCALE = 0.0006f;
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE);
Model::simulate(deltaTime, delayLoad);
if (!_owningAvatar->isMyAvatar()) {
if (!(isActive() && _owningAvatar->isMyAvatar())) {
return; // only simulate for own avatar
}
@ -199,8 +195,9 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
glm::mat3 axes = glm::mat3_cast(_rotation);
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) *
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
state.rotation = glm::angleAxis(-_owningAvatar->getHead()->getLeanSideways(), glm::normalize(inverse * axes[2])) *
glm::angleAxis(-_owningAvatar->getHead()->getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation;
state.rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getLeanSideways(),
glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getLeanForward(),
glm::normalize(inverse * axes[0])) * joint.rotation;
}
void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) {

View file

@ -65,8 +65,8 @@ void Faceshift::update() {
return;
}
// get the euler angles relative to the window
glm::vec3 eulers = safeEulerAngles(_headRotation * glm::quat(glm::radians(glm::vec3(
(_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f))));
glm::vec3 eulers = glm::degrees(safeEulerAngles(_headRotation * glm::quat(glm::radians(glm::vec3(
(_eyeGazeLeftPitch + _eyeGazeRightPitch) / 2.0f, (_eyeGazeLeftYaw + _eyeGazeRightYaw) / 2.0f, 0.0f)))));
// compute and subtract the long term average
const float LONG_TERM_AVERAGE_SMOOTHING = 0.999f;

View file

@ -33,6 +33,7 @@ public:
const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; }
const glm::vec3& getHeadTranslation() const { return _headTranslation; }
// these pitch/yaw angles are in degrees
float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; }
float getEyeGazeLeftYaw() const { return _eyeGazeLeftYaw; }
@ -96,9 +97,9 @@ private:
glm::vec3 _headAngularVelocity;
glm::vec3 _headTranslation;
// degrees
float _eyeGazeLeftPitch;
float _eyeGazeLeftYaw;
float _eyeGazeRightPitch;
float _eyeGazeRightYaw;
@ -121,10 +122,12 @@ private:
int _jawOpenIndex;
// degrees
float _longTermAverageEyePitch;
float _longTermAverageEyeYaw;
bool _longTermAverageInitialized;
// degrees
float _estimatedEyePitch;
float _estimatedEyeYaw;
};

View file

@ -27,7 +27,7 @@ int OculusManager::_scaleLocation;
int OculusManager::_scaleInLocation;
int OculusManager::_hmdWarpParamLocation;
bool OculusManager::_isConnected = false;
float OculusManager::_yawOffset = 0;
float OculusManager::_yawOffset = 0.0f; // radians
#ifdef HAVE_LIBOVR
using namespace OVR;
@ -198,12 +198,7 @@ void OculusManager::updateYawOffset() {
void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) {
#ifdef HAVE_LIBOVR
_sensorFusion->GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
// convert each angle to degrees
// remove the yaw offset from the returned yaw
yaw = glm::degrees(yaw - _yawOffset);
pitch = glm::degrees(pitch);
roll = glm::degrees(roll);
yaw = yaw - _yawOffset;
#endif
}

View file

@ -32,6 +32,9 @@ public:
static void reset();
/// param \yaw[out] yaw in radians
/// param \pitch[out] pitch in radians
/// param \roll[out] roll in radians
static void getEulerAngles(float& yaw, float& pitch, float& roll);
static void updateYawOffset();

View file

@ -89,7 +89,7 @@ void SixenseManager::update(float deltaTime) {
// Rotation of Palm
glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]);
rotation = glm::angleAxis(180.0f, glm::vec3(0.f, 1.f, 0.f)) * rotation;
rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * rotation;
const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f);
glm::vec3 newNormal = rotation * PALM_VECTOR;
palm->setRawNormal(newNormal);

View file

@ -52,7 +52,7 @@ void TV3DManager::setFrustum(Camera& whichCamera) {
double nearZ = whichCamera.getNearClip(); // near clipping plane
double screenZ = Application::getInstance()->getViewFrustum()->getFocalLength(); // screen projection plane
double top = nearZ * tan(DTR * fovy / 2); //sets top of frustum based on fovy and near clipping plane
double top = nearZ * tan(DTR * fovy / 2.0); //sets top of frustum based on fovy and near clipping plane
double right = _aspect * top; // sets right of frustum based on aspect ratio
double frustumshift = (IOD / 2) * nearZ / screenZ;

View file

@ -533,8 +533,8 @@ public:
glm::quat postRotation;
glm::mat4 postTransform;
glm::vec3 rotationMin;
glm::vec3 rotationMax;
glm::vec3 rotationMin; // radians
glm::vec3 rotationMax; // radians
};
glm::mat4 getGlobalTransform(const QMultiHash<QString, QString>& parentMap,
@ -806,7 +806,7 @@ void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) {
}
glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex);
mesh.tangents[firstIndex] += glm::cross(glm::angleAxis(
-glm::degrees(atan2f(-texCoordDelta.t, texCoordDelta.s)), normal) * glm::normalize(bitangent), normal);
- atan2f(-texCoordDelta.t, texCoordDelta.s), normal) * glm::normalize(bitangent), normal);
}
QVector<int> getIndices(const QVector<QString> ids, QVector<QString> modelIDs) {
@ -1000,6 +1000,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
jointRightFingertipIDs[index] = getID(object.properties);
}
glm::vec3 translation;
// NOTE: the euler angles as supplied by the FBX file are in degrees
glm::vec3 rotationOffset;
glm::vec3 preRotation, rotation, postRotation;
glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f);
@ -1054,7 +1055,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} else if (property.properties.at(0) == "RotationMin") {
rotationMin = getVec3(property.properties, index);
} else if (property.properties.at(0) == "RotationMax") {
}
// NOTE: these rotation limits are stored in degrees (NOT radians)
else if (property.properties.at(0) == "RotationMax") {
rotationMax = getVec3(property.properties, index);
} else if (property.properties.at(0) == "RotationMinX") {
@ -1104,10 +1107,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
model.postRotation = glm::quat(glm::radians(postRotation));
model.postTransform = glm::translate(-rotationPivot) * glm::translate(scalePivot) *
glm::scale(scale) * glm::translate(-scalePivot);
model.rotationMin = glm::vec3(rotationMinX ? rotationMin.x : -180.0f,
rotationMinY ? rotationMin.y : -180.0f, rotationMinZ ? rotationMin.z : -180.0f);
model.rotationMax = glm::vec3(rotationMaxX ? rotationMax.x : 180.0f,
rotationMaxY ? rotationMax.y : 180.0f, rotationMaxZ ? rotationMax.z : 180.0f);
// NOTE: anbgles from the FBX file are in degrees
// so we convert them to radians for the FBXModel class
model.rotationMin = glm::radians(glm::vec3(rotationMinX ? rotationMin.x : -180.0f,
rotationMinY ? rotationMin.y : -180.0f, rotationMinZ ? rotationMin.z : -180.0f));
model.rotationMax = glm::radians(glm::vec3(rotationMaxX ? rotationMax.x : 180.0f,
rotationMaxY ? rotationMax.y : 180.0f, rotationMaxZ ? rotationMax.z : 180.0f));
models.insert(getID(object.properties), model);
} else if (object.name == "Texture") {
@ -1324,6 +1329,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.bindExtents.reset();
geometry.staticExtents.reset();
geometry.meshExtents.reset();
QVariantHash springs = mapping.value("spring").toHash();
QVariant defaultSpring = springs.value("default");
@ -1335,6 +1341,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
extracted.mesh.springiness = springs.value(models.value(modelID).name, defaultSpring).toFloat();
glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID);
// compute the mesh extents from the transformed vertices
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
glm::vec3 transformedVertex = glm::vec3(modelTransform * glm::vec4(vertex, 1.0f));
geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex);
geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex);
}
// look for textures, material properties
int materialIndex = 0;
int textureIndex = 0;
@ -1812,5 +1825,7 @@ FBXGeometry readSVO(const QByteArray& model) {
geometry.meshes.append(mesh);
geometry.meshExtents.maximum = glm::vec3(1.0f, 1.0f, 1.0f);
return geometry;
}

View file

@ -76,8 +76,8 @@ public:
glm::quat postRotation;
glm::mat4 postTransform;
glm::mat4 transform;
glm::vec3 rotationMin;
glm::vec3 rotationMax;
glm::vec3 rotationMin; // radians
glm::vec3 rotationMax; // radians
glm::quat inverseDefaultRotation;
glm::quat inverseBindRotation;
glm::mat4 bindTransform;
@ -179,6 +179,7 @@ public:
Extents bindExtents;
Extents staticExtents;
Extents meshExtents;
QVector<FBXAttachment> attachments;

View file

@ -30,11 +30,11 @@ void GeometryCache::renderHemisphere(int slices, int stacks) {
GLfloat* vertexData = new GLfloat[vertices * 3];
GLfloat* vertex = vertexData;
for (int i = 0; i < stacks - 1; i++) {
float phi = PIf * 0.5f * i / (stacks - 1);
float phi = PI_OVER_TWO * float(i) / float(stacks - 1);
float z = sinf(phi), radius = cosf(phi);
for (int j = 0; j < slices; j++) {
float theta = PIf * 2.0f * j / slices;
float theta = TWO_PI * float(j) / float(slices);
*(vertex++) = sinf(theta) * radius;
*(vertex++) = cosf(theta) * radius;
@ -180,7 +180,7 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) {
float y = (float)i / (stacks - 1);
for (int j = 0; j <= slices; j++) {
float theta = 3 * PIf / 2 + PIf * j / slices;
float theta = 3.f * PI_OVER_TWO + PI * float(j) / float(slices);
//normals
*(vertex++) = sinf(theta);
@ -378,30 +378,6 @@ QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance
return lod;
}
glm::vec4 NetworkGeometry::computeAverageColor() const {
glm::vec4 totalColor;
int totalTriangles = 0;
for (int i = 0; i < _meshes.size(); i++) {
const FBXMesh& mesh = _geometry.meshes.at(i);
if (mesh.isEye) {
continue; // skip eyes
}
const NetworkMesh& networkMesh = _meshes.at(i);
for (int j = 0; j < mesh.parts.size(); j++) {
const FBXMeshPart& part = mesh.parts.at(j);
const NetworkMeshPart& networkPart = networkMesh.parts.at(j);
glm::vec4 color = glm::vec4(part.diffuseColor, 1.0f);
if (networkPart.diffuseTexture) {
color *= networkPart.diffuseTexture->getAverageColor();
}
int triangles = part.quadIndices.size() * 2 + part.triangleIndices.size();
totalColor += color * (float) triangles;
totalTriangles += triangles;
}
}
return (totalTriangles == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / (float) totalTriangles;
}
void NetworkGeometry::setLoadPriority(const QPointer<QObject>& owner, float priority) {
Resource::setLoadPriority(owner, priority);

View file

@ -79,9 +79,6 @@ public:
const FBXGeometry& getFBXGeometry() const { return _geometry; }
const QVector<NetworkMesh>& getMeshes() const { return _meshes; }
/// Returns the average color of all meshes in the geometry.
glm::vec4 computeAverageColor() const;
virtual void setLoadPriority(const QPointer<QObject>& owner, float priority);
virtual void setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities);
virtual void clearLoadPriority(const QPointer<QObject>& owner);

View file

@ -497,10 +497,6 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
}
}
glm::vec4 Model::computeAverageColor() const {
return _geometry ? _geometry->computeAverageColor() : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
}
bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
const glm::vec3 relativeOrigin = origin - _translation;
const FBXGeometry& geometry = _geometry->getFBXGeometry();
@ -760,15 +756,15 @@ float Model::getLimbLength(int jointIndex) const {
void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain) {
JointState& state = _jointStates[jointIndex];
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
if (!constrain || (joint.rotationMin == glm::vec3(-180.0f, -180.0f, -180.0f) &&
joint.rotationMax == glm::vec3(180.0f, 180.0f, 180.0f))) {
if (!constrain || (joint.rotationMin == glm::vec3(-PI, -PI, -PI) &&
joint.rotationMax == glm::vec3(PI, PI, PI))) {
// no constraints
state.rotation = state.rotation * glm::inverse(state.combinedRotation) * delta * state.combinedRotation;
state.combinedRotation = delta * state.combinedRotation;
return;
}
glm::quat newRotation = glm::quat(glm::radians(glm::clamp(safeEulerAngles(state.rotation *
glm::inverse(state.combinedRotation) * delta * state.combinedRotation), joint.rotationMin, joint.rotationMax)));
glm::quat newRotation = glm::quat(glm::clamp(safeEulerAngles(state.rotation *
glm::inverse(state.combinedRotation) * delta * state.combinedRotation), joint.rotationMin, joint.rotationMax));
state.combinedRotation = state.combinedRotation * glm::inverse(state.rotation) * newRotation;
state.rotation = newRotation;
}
@ -789,7 +785,7 @@ void Model::renderCollisionProxies(float alpha) {
glTranslatef(position.x, position.y, position.z);
const glm::quat& rotation = shape->getRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
// draw a grey sphere at shape position
glColor4f(0.75f, 0.75f, 0.75f, alpha);
@ -864,7 +860,7 @@ void Model::applyCollision(CollisionInfo& collision) {
axis = glm::normalize(axis);
glm::vec3 end;
getJointPosition(jointIndex, end);
glm::vec3 newEnd = start + glm::angleAxis(glm::degrees(angle), axis) * (end - start);
glm::vec3 newEnd = start + glm::angleAxis(angle, axis) * (end - start);
// try to move it
setJointPosition(jointIndex, newEnd, -1, true);
}

View file

@ -175,9 +175,6 @@ public:
/// Returns the extended length from the right hand to its first free ancestor.
float getRightArmLength() const;
/// Returns the average color of all meshes in the geometry.
glm::vec4 computeAverageColor() const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
/// \param shapes list of pointers shapes to test against Model

View file

@ -113,8 +113,7 @@ GLuint TextureCache::getFileTextureID(const QString& filename) {
glBindTexture(GL_TEXTURE_2D, id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,
GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
_fileTextureIDs.insert(filename, id);
@ -257,7 +256,6 @@ Texture::~Texture() {
NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
Resource(url),
_averageColor(1.0f, 1.0f, 1.0f, 1.0f),
_translucent(false) {
if (!url.isValid()) {
@ -300,27 +298,20 @@ void ImageReader::run() {
image = image.convertToFormat(QImage::Format_ARGB32);
}
// sum up the colors for the average and check for translucency
glm::vec4 accumulated;
// check for translucency
int translucentPixels = 0;
const int EIGHT_BIT_MAXIMUM = 255;
const int RGB_BITS = 24;
for (int y = 0; y < image.height(); y++) {
for (int x = 0; x < image.width(); x++) {
QRgb pixel = image.pixel(x, y);
accumulated.r += qRed(pixel);
accumulated.g += qGreen(pixel);
accumulated.b += qBlue(pixel);
int alpha = qAlpha(pixel);
int alpha = image.pixel(x, y) >> RGB_BITS;
if (alpha != 0 && alpha != EIGHT_BIT_MAXIMUM) {
translucentPixels++;
}
accumulated.a += alpha;
}
}
int imageArea = image.width() * image.height();
QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image),
Q_ARG(const glm::vec4&, accumulated / (float) (imageArea * EIGHT_BIT_MAXIMUM)),
Q_ARG(bool, translucentPixels >= imageArea / 2));
_reply->deleteLater();
}
@ -330,8 +321,7 @@ void NetworkTexture::downloadFinished(QNetworkReply* reply) {
QThreadPool::globalInstance()->start(new ImageReader(_self, reply));
}
void NetworkTexture::setImage(const QImage& image, const glm::vec4& averageColor, bool translucent) {
_averageColor = averageColor;
void NetworkTexture::setImage(const QImage& image, bool translucent) {
_translucent = translucent;
finishedLoading(true);
@ -339,8 +329,7 @@ void NetworkTexture::setImage(const QImage& image, const glm::vec4& averageColor
glBindTexture(GL_TEXTURE_2D, getID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 1,
GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}
@ -373,8 +362,7 @@ QSharedPointer<Texture> DilatableNetworkTexture::getDilatedTexture(float dilatio
glBindTexture(GL_TEXTURE_2D, texture->getID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dilatedImage.width(), dilatedImage.height(), 1,
GL_BGRA, GL_UNSIGNED_BYTE, dilatedImage.constBits());
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}

View file

@ -119,9 +119,6 @@ public:
NetworkTexture(const QUrl& url, bool normalMap);
/// Returns the average color over the entire texture.
const glm::vec4& getAverageColor() const { return _averageColor; }
/// Checks whether it "looks like" this texture is translucent
/// (majority of pixels neither fully opaque or fully transparent).
bool isTranslucent() const { return _translucent; }
@ -131,11 +128,10 @@ protected:
virtual void downloadFinished(QNetworkReply* reply);
virtual void imageLoaded(const QImage& image);
Q_INVOKABLE void setImage(const QImage& image, const glm::vec4& averageColor, bool translucent);
Q_INVOKABLE void setImage(const QImage& image, bool translucent);
private:
glm::vec4 _averageColor;
bool _translucent;
};

View file

@ -16,6 +16,7 @@
using namespace starfield;
const float Generator::STAR_COLORIZATION = 0.1f;
const float PI_OVER_180 = 3.14159265358979f / 180.f;
void Generator::computeStarPositions(InputVertices& destination, unsigned limit, unsigned seed) {
InputVertices* vertices = & destination;

View file

@ -1,102 +0,0 @@
//
// ChatEntry.cpp
// interface
//
// Created by Andrzej Kapolka on 4/24/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
#include <QKeyEvent>
#include "ChatEntry.h"
#include "InterfaceConfig.h"
#include "Util.h"
using namespace std;
const int MAX_CONTENT_LENGTH = 80;
ChatEntry::ChatEntry() : _cursorPos(0) {
}
void ChatEntry::clear() {
_contents.clear();
_cursorPos = 0;
}
bool ChatEntry::keyPressEvent(QKeyEvent* event) {
event->accept();
switch (event->key()) {
case Qt::Key_Return:
case Qt::Key_Enter:
return false;
case Qt::Key_Escape:
clear();
return false;
case Qt::Key_Backspace:
if (_cursorPos != 0) {
_contents.erase(_cursorPos - 1, 1);
_cursorPos--;
}
return true;
case Qt::Key_Delete:
if (_cursorPos < (int)_contents.size()) {
_contents.erase(_cursorPos, 1);
}
return true;
case Qt::Key_Left:
if (_cursorPos != 0) {
_cursorPos--;
}
return true;
case Qt::Key_Right:
if (_cursorPos != _contents.size()) {
_cursorPos++;
}
return true;
default:
QString text = event->text();
if (text.isEmpty()) {
event->ignore();
return true;
}
if (_contents.size() < MAX_CONTENT_LENGTH) {
_contents.insert(_cursorPos, 1, text.at(0).toLatin1());
_cursorPos++;
}
return true;
}
}
const float ALL_WHITE[] = { 1.0f, 1.0f, 1.0f };
void ChatEntry::render(int screenWidth, int screenHeight) {
// draw a gray background so that we can actually see what we're typing
int bottom = screenHeight - 150, top = screenHeight - 165;
int left = 20, right = left + 600;
glColor3f(0.2f, 0.2f, 0.2f);
glBegin(GL_QUADS);
glVertex2f(left - 5, bottom + 7);
glVertex2f(right + 5, bottom + 7);
glVertex2f(right + 5, top - 3);
glVertex2f(left - 5, top - 3);
glEnd();
drawText(left, bottom, 0.10f, 0, 2, _contents.c_str(), ALL_WHITE);
float width = 0;
for (string::iterator it = _contents.begin(), end = it + _cursorPos; it != end; it++) {
width += widthChar(0.10f, 0, *it);
}
glDisable(GL_LINE_SMOOTH);
glBegin(GL_LINE_STRIP);
glVertex2f(left + width, top + 2);
glVertex2f(left + width, bottom + 2);
glEnd();
}

View file

@ -1,35 +0,0 @@
//
// ChatEntry.h
// interface
//
// Created by Andrzej Kapolka on 4/24/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__ChatEntry__
#define __interface__ChatEntry__
#include <string>
class QKeyEvent;
class ChatEntry {
public:
ChatEntry();
const std::string& getContents() const { return _contents; }
void clear();
bool keyPressEvent(QKeyEvent* event);
void render(int screenWidth, int screenHeight);
private:
std::string _contents;
int _cursorPos;
};
#endif /* defined(__interface__ChatEntry__) */

View file

@ -179,6 +179,10 @@ void ChatWindow::participantsChanged() {
}
void ChatWindow::messageReceived(const QXmppMessage& message) {
if (message.type() != QXmppMessage::GroupChat) {
return;
}
QLabel* userLabel = new QLabel(getParticipantName(message.from()));
QFont font = userLabel->font();
font.setBold(true);

View file

@ -100,6 +100,7 @@ MetavoxelEditor::MetavoxelEditor() :
addTool(new InsertSpannerTool(this));
addTool(new RemoveSpannerTool(this));
addTool(new ClearSpannersTool(this));
addTool(new SetSpannerTool(this));
updateAttributes();
@ -138,11 +139,11 @@ glm::quat MetavoxelEditor::getGridRotation() const {
return glm::quat();
case GRID_PLANE_XZ:
return glm::angleAxis(-90.0f, glm::vec3(1.0f, 0.0f, 0.0f));
return glm::angleAxis(-PI_OVER_TWO, glm::vec3(1.0f, 0.0f, 0.0f));
case GRID_PLANE_YZ:
default:
return glm::angleAxis(90.0f, glm::vec3(0.0f, 1.0f, 0.0f));
return glm::angleAxis(PI_OVER_TWO, glm::vec3(0.0f, 1.0f, 0.0f));
}
}
@ -269,18 +270,19 @@ const float GRID_BRIGHTNESS = 0.5f;
void MetavoxelEditor::render() {
glDisable(GL_LIGHTING);
MetavoxelTool* tool = getActiveTool();
if (tool) {
tool->render();
}
glDepthMask(GL_FALSE);
glPushMatrix();
glm::quat rotation = getGridRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
MetavoxelTool* tool = getActiveTool();
if (tool) {
tool->render();
}
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glLineWidth(1.0f);
@ -370,12 +372,23 @@ BoxSetTool::BoxSetTool(MetavoxelEditor* editor) :
}
void BoxSetTool::render() {
if (Application::getInstance()->isMouseHidden()) {
resetState();
return;
}
QString selected = _editor->getSelectedAttribute();
if (selected.isNull()) {
resetState();
return;
}
glDepthMask(GL_FALSE);
glPushMatrix();
glm::quat rotation = _editor->getGridRotation();
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z);
glm::quat inverseRotation = glm::inverse(rotation);
glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin();
glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection();
@ -439,6 +452,8 @@ void BoxSetTool::render() {
glPopMatrix();
}
glPopMatrix();
}
bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) {
@ -515,20 +530,18 @@ void GlobalSetTool::apply() {
Application::getInstance()->getMetavoxels()->applyEdit(message);
}
InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Insert Spanner") {
PlaceSpannerTool::PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText) :
MetavoxelTool(editor, name) {
QPushButton* button = new QPushButton("Insert");
QPushButton* button = new QPushButton(placeText);
layout()->addWidget(button);
connect(button, SIGNAL(clicked()), SLOT(insert()));
connect(button, SIGNAL(clicked()), SLOT(place()));
}
void InsertSpannerTool::simulate(float deltaTime) {
SharedObjectPointer spanner = _editor->getValue().value<SharedObjectPointer>();
static_cast<Spanner*>(spanner.data())->getRenderer()->simulate(deltaTime);
void PlaceSpannerTool::simulate(float deltaTime) {
if (Application::getInstance()->isMouseHidden()) {
return;
}
void InsertSpannerTool::render() {
_editor->detachValue();
Spanner* spanner = static_cast<Spanner*>(_editor->getValue().value<SharedObjectPointer>().data());
Transformable* transformable = qobject_cast<Transformable*>(spanner);
@ -543,43 +556,70 @@ void InsertSpannerTool::render() {
transformable->setTranslation(rotation * glm::vec3(glm::vec2(rayOrigin + rayDirection * distance), position));
}
spanner->getRenderer()->simulate(deltaTime);
}
void PlaceSpannerTool::render() {
if (Application::getInstance()->isMouseHidden()) {
return;
}
Spanner* spanner = static_cast<Spanner*>(_editor->getValue().value<SharedObjectPointer>().data());
const float SPANNER_ALPHA = 0.25f;
spanner->getRenderer()->render(SPANNER_ALPHA);
}
bool InsertSpannerTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("SharedObjectSetAttribute");
bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("SpannerSetAttribute");
}
bool InsertSpannerTool::eventFilter(QObject* watched, QEvent* event) {
bool PlaceSpannerTool::eventFilter(QObject* watched, QEvent* event) {
if (event->type() == QEvent::MouseButtonPress) {
insert();
place();
return true;
}
return false;
}
void InsertSpannerTool::insert() {
void PlaceSpannerTool::place() {
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
if (!attribute) {
return;
}
SharedObjectPointer spanner = _editor->getValue().value<SharedObjectPointer>();
MetavoxelEditMessage message = { QVariant::fromValue(InsertSpannerEdit(attribute, spanner)) };
MetavoxelEditMessage message = { createEdit(attribute, spanner) };
Application::getInstance()->getMetavoxels()->applyEdit(message);
}
InsertSpannerTool::InsertSpannerTool(MetavoxelEditor* editor) :
PlaceSpannerTool(editor, "Insert Spanner", "Insert") {
}
QVariant InsertSpannerTool::createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
return QVariant::fromValue(InsertSpannerEdit(attribute, spanner));
}
RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Remove Spanner", false) {
}
bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("SharedObjectSetAttribute");
return attribute->inherits("SpannerSetAttribute");
}
bool RemoveSpannerTool::eventFilter(QObject* watched, QEvent* event) {
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
if (!attribute) {
return false;
}
if (event->type() == QEvent::MouseButtonPress) {
float distance;
SharedObjectPointer spanner = Application::getInstance()->getMetavoxels()->findFirstRaySpannerIntersection(
Application::getInstance()->getMouseRayOrigin(), Application::getInstance()->getMouseRayDirection(),
attribute, distance);
if (spanner) {
MetavoxelEditMessage message = { QVariant::fromValue(RemoveSpannerEdit(attribute, spanner->getRemoteID())) };
Application::getInstance()->getMetavoxels()->applyEdit(message);
}
return true;
}
return false;
@ -594,7 +634,7 @@ ClearSpannersTool::ClearSpannersTool(MetavoxelEditor* editor) :
}
bool ClearSpannersTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("SharedObjectSetAttribute");
return attribute->inherits("SpannerSetAttribute");
}
void ClearSpannersTool::clear() {
@ -605,3 +645,16 @@ void ClearSpannersTool::clear() {
MetavoxelEditMessage message = { QVariant::fromValue(ClearSpannersEdit(attribute)) };
Application::getInstance()->getMetavoxels()->applyEdit(message);
}
SetSpannerTool::SetSpannerTool(MetavoxelEditor* editor) :
PlaceSpannerTool(editor, "Set Spanner", "Set") {
}
bool SetSpannerTool::appliesTo(const AttributePointer& attribute) const {
return attribute == AttributeRegistry::getInstance()->getSpannersAttribute();
}
QVariant SetSpannerTool::createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
static_cast<Spanner*>(spanner.data())->setGranularity(_editor->getGridSpacing());
return QVariant::fromValue(SetSpannerEdit(spanner));
}

View file

@ -139,13 +139,13 @@ private slots:
void apply();
};
/// Allows inserting a spanner into the scene.
class InsertSpannerTool : public MetavoxelTool {
/// Base class for insert/set spanner tools.
class PlaceSpannerTool : public MetavoxelTool {
Q_OBJECT
public:
InsertSpannerTool(MetavoxelEditor* editor);
PlaceSpannerTool(MetavoxelEditor* editor, const QString& name, const QString& placeText);
virtual void simulate(float deltaTime);
@ -155,9 +155,26 @@ public:
virtual bool eventFilter(QObject* watched, QEvent* event);
protected:
virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) = 0;
private slots:
void insert();
void place();
};
/// Allows inserting a spanner into the scene.
class InsertSpannerTool : public PlaceSpannerTool {
Q_OBJECT
public:
InsertSpannerTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
};
/// Allows removing a spanner from the scene.
@ -188,4 +205,19 @@ private slots:
void clear();
};
/// Allows setting the value by placing a spanner.
class SetSpannerTool : public PlaceSpannerTool {
Q_OBJECT
public:
SetSpannerTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QVariant createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
};
#endif /* defined(__interface__MetavoxelEditor__) */

View file

@ -9,10 +9,6 @@
#ifndef __interface__world__
#define __interface__world__
#ifndef PIf
#define PIf 3.14159265f
#endif
const float GRAVITY_EARTH = 9.80665f;
const float EDGE_SIZE_GROUND_PLANE = 20.f;

View file

@ -41,7 +41,7 @@ Sound::Sound(float volume, float frequency, float duration, float decay, QObject
int numSamples = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; // we add sounds in chunks of this many samples
int chunkStartingSample = 0;
float waveFrequency = (frequency / SAMPLE_RATE) * PI_TIMES_TWO;
float waveFrequency = (frequency / SAMPLE_RATE) * TWO_PI;
while (volume > 0.f) {
for (int i = 0; i < numSamples; i++) {
t = (float)chunkStartingSample + (float)i;

View file

@ -31,9 +31,9 @@ QNetworkAccessManager* AvatarData::networkAccessManager = NULL;
AvatarData::AvatarData() :
NodeData(),
_handPosition(0,0,0),
_bodyYaw(-90.0),
_bodyPitch(0.0),
_bodyRoll(0.0),
_bodyYaw(-90.f),
_bodyPitch(0.0f),
_bodyRoll(0.0f),
_targetScale(1.0f),
_handState(0),
_keyState(NO_KEY_DOWN),
@ -508,7 +508,7 @@ void AvatarData::setClampedTargetScale(float targetScale) {
}
void AvatarData::setOrientation(const glm::quat& orientation) {
glm::vec3 eulerAngles = safeEulerAngles(orientation);
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(orientation));
_bodyPitch = eulerAngles.x;
_bodyYaw = eulerAngles.y;
_bodyRoll = eulerAngles.z;

View file

@ -108,7 +108,7 @@ public:
QByteArray toByteArray();
int parseData(const QByteArray& packet);
// Body Rotation
// Body Rotation (degrees)
float getBodyYaw() const { return _bodyYaw; }
void setBodyYaw(float bodyYaw) { _bodyYaw = bodyYaw; }
float getBodyPitch() const { return _bodyPitch; }
@ -122,7 +122,7 @@ public:
glm::quat getHeadOrientation() const { return _headData->getOrientation(); }
void setHeadOrientation(const glm::quat& orientation) { _headData->setOrientation(orientation); }
// access to Head().set/getMousePitch
// access to Head().set/getMousePitch (degrees)
float getHeadPitch() const { return _headData->getPitch(); }
void setHeadPitch(float value) { _headData->setPitch(value); };
@ -217,9 +217,9 @@ protected:
glm::vec3 _handPosition;
// Body rotation
float _bodyYaw;
float _bodyPitch;
float _bodyRoll;
float _bodyYaw; // degrees
float _bodyPitch; // degrees
float _bodyRoll; // degrees
// Body scale
float _targetScale;

View file

@ -17,9 +17,9 @@ HeadData::HeadData(AvatarData* owningAvatar) :
_yaw(0.0f),
_pitch(0.0f),
_roll(0.0f),
_lookAtPosition(0.0f, 0.0f, 0.0f),
_leanSideways(0.0f),
_leanForward(0.0f),
_lookAtPosition(0.0f, 0.0f, 0.0f),
_audioLoudness(0.0f),
_isFaceshiftConnected(false),
_leftEyeBlink(0.0f),
@ -39,12 +39,11 @@ void HeadData::setOrientation(const glm::quat& orientation) {
// rotate body about vertical axis
glm::quat bodyOrientation = _owningAvatar->getOrientation();
glm::vec3 newFront = glm::inverse(bodyOrientation) * (orientation * IDENTITY_FRONT);
bodyOrientation = bodyOrientation * glm::angleAxis(glm::degrees(atan2f(-newFront.x, -newFront.z)),
glm::vec3(0.0f, 1.0f, 0.0f));
bodyOrientation = bodyOrientation * glm::angleAxis(atan2f(-newFront.x, -newFront.z), glm::vec3(0.0f, 1.0f, 0.0f));
_owningAvatar->setOrientation(bodyOrientation);
// the rest goes to the head
glm::vec3 eulers = safeEulerAngles(glm::inverse(bodyOrientation) * orientation);
glm::vec3 eulers = glm::degrees(safeEulerAngles(glm::inverse(bodyOrientation) * orientation));
_pitch = eulers.x;
_yaw = eulers.y;
_roll = eulers.z;

View file

@ -15,12 +15,13 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
const float MIN_HEAD_YAW = -110;
const float MAX_HEAD_YAW = 110;
const float MIN_HEAD_PITCH = -60;
const float MAX_HEAD_PITCH = 60;
const float MIN_HEAD_ROLL = -50;
const float MAX_HEAD_ROLL = 50;
// degrees
const float MIN_HEAD_YAW = -110.f;
const float MAX_HEAD_YAW = 110.f;
const float MIN_HEAD_PITCH = -60.f;
const float MAX_HEAD_PITCH = 60.f;
const float MIN_HEAD_ROLL = -50.f;
const float MAX_HEAD_ROLL = 50.f;
class AvatarData;
@ -29,21 +30,17 @@ public:
HeadData(AvatarData* owningAvatar);
virtual ~HeadData() { };
// degrees
float getLeanSideways() const { return _leanSideways; }
void setLeanSideways(float leanSideways) { _leanSideways = leanSideways; }
float getLeanForward() const { return _leanForward; }
void setLeanForward(float leanForward) { _leanForward = leanForward; }
float getYaw() const { return _yaw; }
void setYaw(float yaw) { _yaw = glm::clamp(yaw, MIN_HEAD_YAW, MAX_HEAD_YAW); }
float getPitch() const { return _pitch; }
void setPitch(float pitch) { _pitch = glm::clamp(pitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH); }
float getRoll() const { return _roll; }
void setRoll(float roll) { _roll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); }
virtual float getTweakedYaw() const { return _yaw; }
virtual float getTweakedPitch() const { return _pitch; }
virtual float getTweakedRoll() const { return _roll; }
@ -62,6 +59,7 @@ public:
float getPupilDilation() const { return _pupilDilation; }
void setPupilDilation(float pupilDilation) { _pupilDilation = pupilDilation; }
// degrees
void addYaw(float yaw);
void addPitch(float pitch);
void addRoll(float roll);
@ -73,12 +71,14 @@ public:
friend class AvatarData;
protected:
// degrees
float _yaw;
float _pitch;
float _roll;
glm::vec3 _lookAtPosition;
float _leanSideways;
float _leanForward;
glm::vec3 _lookAtPosition;
float _audioLoudness;
bool _isFaceshiftConnected;
float _leftEyeBlink;

View file

@ -12,8 +12,10 @@
#include "MetavoxelData.h"
REGISTER_META_OBJECT(QRgbAttribute)
REGISTER_META_OBJECT(PackedNormalAttribute)
REGISTER_META_OBJECT(SharedObjectAttribute)
REGISTER_META_OBJECT(SharedObjectSetAttribute)
REGISTER_META_OBJECT(SpannerSetAttribute)
AttributeRegistry* AttributeRegistry::getInstance() {
static AttributeRegistry registry;
@ -23,9 +25,13 @@ AttributeRegistry* AttributeRegistry::getInstance() {
AttributeRegistry::AttributeRegistry() :
_guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject,
SharedObjectPointer(new DefaultMetavoxelGuide())))),
_spannersAttribute(registerAttribute(new SharedObjectSetAttribute("spanners", &Spanner::staticMetaObject))),
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))),
_colorAttribute(registerAttribute(new QRgbAttribute("color"))),
_normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) {
_normalAttribute(registerAttribute(new PackedNormalAttribute("normal", qRgb(0, 127, 0)))) {
// our baseline LOD threshold is for voxels; spanners are a different story
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 4.0f;
_spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER);
}
static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) {
@ -48,6 +54,9 @@ void AttributeRegistry::configureScriptEngine(QScriptEngine* engine) {
}
AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute) {
if (!attribute) {
return attribute;
}
AttributePointer& pointer = _attributes[attribute->getName()];
if (!pointer) {
pointer = attribute;
@ -138,13 +147,38 @@ OwnedAttributeValue& OwnedAttributeValue::operator=(const OwnedAttributeValue& o
return *this;
}
Attribute::Attribute(const QString& name) {
Attribute::Attribute(const QString& name) :
_lodThresholdMultiplier(1.0f) {
setObjectName(name);
}
Attribute::~Attribute() {
}
void Attribute::read(MetavoxelData& data, MetavoxelStreamState& state) {
data.createRoot(state.attribute)->read(state);
}
void Attribute::write(const MetavoxelNode& root, MetavoxelStreamState& state) {
root.write(state);
}
void Attribute::readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) {
data.createRoot(state.attribute)->readDelta(reference, state);
}
void Attribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) {
root.writeDelta(reference, state);
}
void Attribute::readSubdivision(MetavoxelData& data, MetavoxelStreamState& state) {
data.getRoot(state.attribute)->readSubdivision(state);
}
void Attribute::writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) {
root.writeSubdivision(state);
}
QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) :
InlineAttribute<QRgb>(name, defaultValue) {
}
@ -189,6 +223,39 @@ QWidget* QRgbAttribute::createEditor(QWidget* parent) const {
return editor;
}
PackedNormalAttribute::PackedNormalAttribute(const QString& name, QRgb defaultValue) :
QRgbAttribute(name, defaultValue) {
}
bool PackedNormalAttribute::merge(void*& parent, void* children[]) const {
QRgb firstValue = decodeInline<QRgb>(children[0]);
int totalRed = (char)qRed(firstValue);
int totalGreen = (char)qGreen(firstValue);
int totalBlue = (char)qBlue(firstValue);
bool allChildrenEqual = true;
for (int i = 1; i < Attribute::MERGE_COUNT; i++) {
QRgb value = decodeInline<QRgb>(children[i]);
totalRed += (char)qRed(value);
totalGreen += (char)qGreen(value);
totalBlue += (char)qBlue(value);
allChildrenEqual &= (firstValue == value);
}
parent = encodeInline(packNormal(glm::normalize(glm::vec3(totalRed, totalGreen, totalBlue))));
return allChildrenEqual;
}
const float CHAR_SCALE = 127.0f;
const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE;
QRgb packNormal(const glm::vec3& normal) {
return qRgb((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE));
}
glm::vec3 unpackNormal(QRgb value) {
return glm::vec3((char)qRed(value) * INVERSE_CHAR_SCALE, (char)qGreen(value) * INVERSE_CHAR_SCALE,
(char)qBlue(value) * INVERSE_CHAR_SCALE);
}
SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject,
const SharedObjectPointer& defaultValue) :
InlineAttribute<SharedObjectPointer>(name, defaultValue),
@ -255,3 +322,59 @@ bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const {
QWidget* SharedObjectSetAttribute::createEditor(QWidget* parent) const {
return new SharedObjectEditor(_metaObject, parent);
}
SpannerSetAttribute::SpannerSetAttribute(const QString& name, const QMetaObject* metaObject) :
SharedObjectSetAttribute(name, metaObject) {
}
void SpannerSetAttribute::read(MetavoxelData& data, MetavoxelStreamState& state) {
forever {
SharedObjectPointer object;
state.stream >> object;
if (!object) {
break;
}
data.insert(state.attribute, object);
}
}
void SpannerSetAttribute::write(const MetavoxelNode& root, MetavoxelStreamState& state) {
Spanner::incrementVisit();
root.writeSpanners(state);
state.stream << SharedObjectPointer();
}
void SpannerSetAttribute::readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state) {
forever {
SharedObjectPointer object;
state.stream >> object;
if (!object) {
break;
}
data.toggle(state.attribute, object);
}
}
void SpannerSetAttribute::writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state) {
Spanner::incrementVisit();
root.writeSpannerDelta(reference, state);
state.stream << SharedObjectPointer();
}
void SpannerSetAttribute::readSubdivision(MetavoxelData& data, MetavoxelStreamState& state) {
forever {
SharedObjectPointer object;
state.stream >> object;
if (!object) {
break;
}
data.insert(state.attribute, object);
}
}
void SpannerSetAttribute::writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) {
Spanner::incrementVisit();
root.writeSpannerSubdivision(state);
state.stream << SharedObjectPointer();
}

View file

@ -23,6 +23,9 @@ class QScriptEngine;
class QScriptValue;
class Attribute;
class MetavoxelData;
class MetavoxelNode;
class MetavoxelStreamState;
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
@ -150,6 +153,7 @@ public:
/// Represents a registered attribute.
class Attribute : public SharedObject {
Q_OBJECT
Q_PROPERTY(float lodThresholdMultiplier MEMBER _lodThresholdMultiplier)
public:
@ -160,6 +164,9 @@ public:
Q_INVOKABLE QString getName() const { return objectName(); }
float getLODThresholdMultiplier() const { return _lodThresholdMultiplier; }
void setLODThresholdMultiplier(float multiplier) { _lodThresholdMultiplier = multiplier; }
void* create() const { return create(getDefaultValue()); }
virtual void* create(void* copy) const = 0;
virtual void destroy(void* value) const = 0;
@ -170,6 +177,15 @@ public:
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); }
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); }
virtual void read(MetavoxelData& data, MetavoxelStreamState& state);
virtual void write(const MetavoxelNode& root, MetavoxelStreamState& state);
virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state);
virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state);
virtual void readSubdivision(MetavoxelData& data, MetavoxelStreamState& state);
virtual void writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state);
virtual bool equal(void* first, void* second) const = 0;
/// Merges the value of a parent and its children.
@ -185,6 +201,10 @@ public:
/// Creates a widget to use to edit values of this attribute, or returns NULL if the attribute isn't editable.
/// The widget should have a single "user" property that will be used to get/set the value.
virtual QWidget* createEditor(QWidget* parent = NULL) const { return NULL; }
private:
float _lodThresholdMultiplier;
};
/// A simple attribute class that stores its values inline.
@ -260,6 +280,23 @@ public:
virtual QWidget* createEditor(QWidget* parent = NULL) const;
};
/// Provides appropriate averaging for packed normals.
class PackedNormalAttribute : public QRgbAttribute {
Q_OBJECT
public:
Q_INVOKABLE PackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb());
virtual bool merge(void*& parent, void* children[]) const;
};
/// Packs a normal into an RGB value.
QRgb packNormal(const glm::vec3& normal);
/// Unpacks a normal from an RGB value.
glm::vec3 unpackNormal(QRgb value);
/// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject).
class SharedObjectAttribute : public InlineAttribute<SharedObjectPointer> {
Q_OBJECT
@ -309,4 +346,23 @@ private:
const QMetaObject* _metaObject;
};
/// An attribute that takes the form of a set of spanners.
class SpannerSetAttribute : public SharedObjectSetAttribute {
Q_OBJECT
public:
Q_INVOKABLE SpannerSetAttribute(const QString& name = QString(),
const QMetaObject* metaObject = &SharedObject::staticMetaObject);
virtual void read(MetavoxelData& data, MetavoxelStreamState& state);
virtual void write(const MetavoxelNode& root, MetavoxelStreamState& state);
virtual void readDelta(MetavoxelData& data, const MetavoxelNode& reference, MetavoxelStreamState& state);
virtual void writeDelta(const MetavoxelNode& root, const MetavoxelNode& reference, MetavoxelStreamState& state);
virtual void readSubdivision(MetavoxelData& data, MetavoxelStreamState& state);
virtual void writeSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state);
};
#endif /* defined(__interface__AttributeRegistry__) */

View file

@ -167,15 +167,19 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) {
_scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets);
_sharedObjectStreamer.persistTransientOffsets(mappings.sharedObjectOffsets);
// find out when shared objects' reference counts drop to one in order to clear their mappings
// find out when shared objects are deleted in order to clear their mappings
for (QHash<SharedObjectPointer, int>::const_iterator it = mappings.sharedObjectOffsets.constBegin();
it != mappings.sharedObjectOffsets.constEnd(); it++) {
if (it.key()) {
connect(it.key().data(), SIGNAL(referenceCountDroppedToOne()), SLOT(clearSharedObject()));
connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*)));
}
}
}
void Bitstream::persistAndResetWriteMappings() {
persistWriteMappings(getAndResetWriteMappings());
}
Bitstream::ReadMappings Bitstream::getAndResetReadMappings() {
ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(),
_typeStreamerStreamer.getAndResetTransientValues(),
@ -193,6 +197,17 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) {
_sharedObjectStreamer.persistTransientValues(mappings.sharedObjectValues);
}
void Bitstream::persistAndResetReadMappings() {
persistReadMappings(getAndResetReadMappings());
}
void Bitstream::clearSharedObject(int id) {
SharedObjectPointer object = _sharedObjectStreamer.takePersistentValue(id);
if (object) {
_weakSharedObjectHash.remove(object->getRemoteID());
}
}
Bitstream& Bitstream::operator<<(bool value) {
if (value) {
_byte |= (1 << _position);
@ -479,7 +494,7 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
object = SharedObjectPointer();
return *this;
}
QPointer<SharedObject>& pointer = _transientSharedObjects[id];
QPointer<SharedObject>& pointer = _weakSharedObjectHash[id];
if (pointer) {
const QMetaObject* metaObject;
_metaObjectStreamer >> metaObject;
@ -492,15 +507,17 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
QObject* rawObject;
*this >> rawObject;
pointer = static_cast<SharedObject*>(rawObject);
pointer->setRemoteID(id);
}
object = static_cast<SharedObject*>(pointer.data());
return *this;
}
void Bitstream::clearSharedObject() {
SharedObjectPointer object(static_cast<SharedObject*>(sender()));
object->disconnect(this);
emit sharedObjectCleared(_sharedObjectStreamer.takePersistentID(object));
void Bitstream::clearSharedObject(QObject* object) {
int id = _sharedObjectStreamer.takePersistentID(static_cast<SharedObject*>(object));
if (id != 0) {
emit sharedObjectCleared(id);
}
}
void Bitstream::readProperties(QObject* object) {

View file

@ -55,7 +55,7 @@ private:
/// Provides a means to stream repeated values efficiently. The value is first streamed along with a unique ID. When
/// subsequently streamed, only the ID is sent.
template<class T> class RepeatedValueStreamer {
template<class T, class P = T> class RepeatedValueStreamer {
public:
RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream),
@ -69,9 +69,9 @@ public:
void persistTransientValues(const QHash<int, T>& transientValues);
int takePersistentID(T value) { return _persistentIDs.take(value); }
int takePersistentID(P value) { return _persistentIDs.take(value); }
void removePersistentValue(int id) { _persistentValues.remove(id); }
T takePersistentValue(int id) { T value = _persistentValues.take(id); _persistentIDs.remove(value); return value; }
RepeatedValueStreamer& operator<<(T value);
RepeatedValueStreamer& operator>>(T& value);
@ -82,13 +82,13 @@ private:
IDStreamer _idStreamer;
int _lastPersistentID;
int _lastTransientOffset;
QHash<T, int> _persistentIDs;
QHash<P, int> _persistentIDs;
QHash<T, int> _transientOffsets;
QHash<int, T> _persistentValues;
QHash<int, T> _transientValues;
};
template<class T> inline QHash<T, int> RepeatedValueStreamer<T>::getAndResetTransientOffsets() {
template<class T, class P> inline QHash<T, int> RepeatedValueStreamer<T, P>::getAndResetTransientOffsets() {
QHash<T, int> transientOffsets;
_transientOffsets.swap(transientOffsets);
_lastTransientOffset = 0;
@ -96,7 +96,8 @@ template<class T> inline QHash<T, int> RepeatedValueStreamer<T>::getAndResetTran
return transientOffsets;
}
template<class T> inline void RepeatedValueStreamer<T>::persistTransientOffsets(const QHash<T, int>& transientOffsets) {
template<class T, class P> inline void RepeatedValueStreamer<T, P>::persistTransientOffsets(
const QHash<T, int>& transientOffsets) {
int oldLastPersistentID = _lastPersistentID;
for (typename QHash<T, int>::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) {
int& id = _persistentIDs[it.key()];
@ -108,14 +109,15 @@ template<class T> inline void RepeatedValueStreamer<T>::persistTransientOffsets(
_idStreamer.setBitsFromValue(_lastPersistentID);
}
template<class T> inline QHash<int, T> RepeatedValueStreamer<T>::getAndResetTransientValues() {
template<class T, class P> inline QHash<int, T> RepeatedValueStreamer<T, P>::getAndResetTransientValues() {
QHash<int, T> transientValues;
_transientValues.swap(transientValues);
_idStreamer.setBitsFromValue(_lastPersistentID);
return transientValues;
}
template<class T> inline void RepeatedValueStreamer<T>::persistTransientValues(const QHash<int, T>& transientValues) {
template<class T, class P> inline void RepeatedValueStreamer<T, P>::persistTransientValues(
const QHash<int, T>& transientValues) {
int oldLastPersistentID = _lastPersistentID;
for (typename QHash<int, T>::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) {
int& id = _persistentIDs[it.value()];
@ -128,7 +130,7 @@ template<class T> inline void RepeatedValueStreamer<T>::persistTransientValues(c
_idStreamer.setBitsFromValue(_lastPersistentID);
}
template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::operator<<(T value) {
template<class T, class P> inline RepeatedValueStreamer<T, P>& RepeatedValueStreamer<T, P>::operator<<(T value) {
int id = _persistentIDs.value(value);
if (id == 0) {
int& offset = _transientOffsets[value];
@ -145,7 +147,7 @@ template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::ope
return *this;
}
template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::operator>>(T& value) {
template<class T, class P> inline RepeatedValueStreamer<T, P>& RepeatedValueStreamer<T, P>::operator>>(T& value) {
int id;
_idStreamer >> id;
if (id <= _lastPersistentID) {
@ -228,14 +230,23 @@ public:
/// Persists a set of write mappings recorded earlier.
void persistWriteMappings(const WriteMappings& mappings);
/// Immediately persists and resets the write mappings.
void persistAndResetWriteMappings();
/// Returns the set of transient mappings gathered during reading and resets them.
ReadMappings getAndResetReadMappings();
/// Persists a set of read mappings recorded earlier.
void persistReadMappings(const ReadMappings& mappings);
/// Immediately persists and resets the read mappings.
void persistAndResetReadMappings();
/// Returns a reference to the weak hash storing shared objects for this stream.
const WeakSharedObjectHash& getWeakSharedObjectHash() const { return _weakSharedObjectHash; }
/// Removes a shared object from the read mappings.
void clearSharedObject(int id) { _sharedObjectStreamer.removePersistentValue(id); }
void clearSharedObject(int id);
Bitstream& operator<<(bool value);
Bitstream& operator>>(bool& value);
@ -315,7 +326,7 @@ signals:
private slots:
void clearSharedObject();
void clearSharedObject(QObject* object);
private:
@ -329,9 +340,9 @@ private:
RepeatedValueStreamer<const TypeStreamer*> _typeStreamerStreamer;
RepeatedValueStreamer<AttributePointer> _attributeStreamer;
RepeatedValueStreamer<QScriptString> _scriptStringStreamer;
RepeatedValueStreamer<SharedObjectPointer> _sharedObjectStreamer;
RepeatedValueStreamer<SharedObjectPointer, SharedObject*> _sharedObjectStreamer;
QHash<int, QPointer<SharedObject> > _transientSharedObjects;
WeakSharedObjectHash _weakSharedObjectHash;
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();

View file

@ -587,6 +587,7 @@ void ReliableChannel::sendMessage(const QVariant& message) {
_dataStream << (quint32)0;
_bitstream << message;
_bitstream.flush();
_bitstream.persistAndResetWriteMappings();
quint32 length = _buffer.pos() - placeholder;
_buffer.writeBytes(placeholder, sizeof(quint32), (const char*)&length);
@ -745,6 +746,7 @@ void ReliableChannel::readData(QDataStream& in) {
QVariant message;
_bitstream >> message;
_bitstream.reset();
_bitstream.persistAndResetReadMappings();
emit receivedMessage(message);
continue;
}

View file

@ -34,6 +34,9 @@ public:
DatagramSequencer(const QByteArray& datagramHeader = QByteArray(), QObject* parent = NULL);
/// Returns a reference to the weak hash mapping remote ids to shared objects.
const WeakSharedObjectHash& getWeakSharedObjectHash() const { return _inputStream.getWeakSharedObjectHash(); }
/// Returns the packet number of the last packet sent.
int getOutgoingPacketNumber() const { return _outgoingPacketNumber; }

File diff suppressed because it is too large Load diff

View file

@ -31,6 +31,26 @@ class NetworkValue;
class Spanner;
class SpannerRenderer;
/// Determines whether to subdivide each node when traversing.
class MetavoxelLOD {
STREAMABLE
public:
STREAM glm::vec3 position;
STREAM float threshold;
MetavoxelLOD(const glm::vec3& position = glm::vec3(), float threshold = 0.0f);
bool isValid() const { return threshold > 0.0f; }
bool shouldSubdivide(const glm::vec3& minimum, float size, float multiplier = 1.0f) const;
/// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference.
bool becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference, float multiplier = 1.0f) const;
};
DECLARE_STREAMABLE_METATYPE(MetavoxelLOD)
/// The base metavoxel representation shared between server and client.
class MetavoxelData {
public:
@ -43,25 +63,40 @@ public:
float getSize() const { return _size; }
glm::vec3 getMinimum() const { return glm::vec3(_size, _size, _size) * -0.5f; }
Box getBounds() const;
/// Applies the specified visitor to the contained voxels.
void guide(MetavoxelVisitor& visitor);
void insert(const AttributePointer& attribute, const SharedObjectPointer& object);
void insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
void remove(const AttributePointer& attribute, const SharedObjectPointer& object);
void remove(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
void toggle(const AttributePointer& attribute, const SharedObjectPointer& object);
void toggle(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
void clear(const AttributePointer& attribute);
/// Convenience function that finds the first spanner intersecting the provided ray.
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
const AttributePointer& attribute, float& distance, const MetavoxelLOD& lod = MetavoxelLOD());
/// Expands the tree, increasing its capacity in all dimensions.
void expand();
void read(Bitstream& in);
void write(Bitstream& out) const;
void read(Bitstream& in, const MetavoxelLOD& lod = MetavoxelLOD());
void write(Bitstream& out, const MetavoxelLOD& lod = MetavoxelLOD()) const;
void readDelta(const MetavoxelData& reference, Bitstream& in);
void writeDelta(const MetavoxelData& reference, Bitstream& out) const;
void readDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& in, const MetavoxelLOD& lod);
void writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD,
Bitstream& out, const MetavoxelLOD& lod) const;
MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); }
MetavoxelNode* createRoot(const AttributePointer& attribute);
private:
@ -74,6 +109,23 @@ private:
QHash<AttributePointer, MetavoxelNode*> _roots;
};
/// Holds the state used in streaming metavoxel data.
class MetavoxelStreamState {
public:
glm::vec3 minimum;
float size;
const AttributePointer& attribute;
Bitstream& stream;
const MetavoxelLOD& lod;
const MetavoxelLOD& referenceLOD;
bool shouldSubdivide() const;
bool shouldSubdivideReference() const;
bool becameSubdivided() const;
void setMinimum(const glm::vec3& lastMinimum, int index);
};
/// A single node within a metavoxel layer.
class MetavoxelNode {
public:
@ -95,11 +147,18 @@ public:
bool isLeaf() const;
void read(const AttributePointer& attribute, Bitstream& in);
void write(const AttributePointer& attribute, Bitstream& out) const;
void read(MetavoxelStreamState& state);
void write(MetavoxelStreamState& state) const;
void readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in);
void writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const;
void readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state);
void writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const;
void readSubdivision(MetavoxelStreamState& state);
void writeSubdivision(MetavoxelStreamState& state) const;
void writeSpanners(MetavoxelStreamState& state) const;
void writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const;
void writeSpannerSubdivision(MetavoxelStreamState& state) const;
/// Increments the node's reference count.
void incrementReferenceCount() { _referenceCount++; }
@ -133,14 +192,31 @@ public:
bool isLeaf;
Box getBounds() const { return Box(minimum, minimum + glm::vec3(size, size, size)); }
glm::vec3 getCenter() const { return minimum + glm::vec3(size, size, size) * 0.5f; }
};
/// Interface for visitors to metavoxels.
/// Base class for visitors to metavoxels.
class MetavoxelVisitor {
public:
/// Encodes a visitation order sequence for the children of a metavoxel.
static int encodeOrder(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth);
/// Encodes a visitation order sequence that visits each child as sorted along the specified direction.
static int encodeOrder(const glm::vec3& direction);
/// The default visitation order.
static const int DEFAULT_ORDER;
/// A special "order" that instructs the guide to stop recursion.
static const int STOP_RECURSION;
/// A special "order" that short-circuits the tour.
static const int SHORT_CIRCUIT;
MetavoxelVisitor(const QVector<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs = QVector<AttributePointer>());
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD());
virtual ~MetavoxelVisitor();
/// Returns a reference to the list of input attributes desired.
@ -149,35 +225,87 @@ public:
/// Returns a reference to the list of output attributes provided.
const QVector<AttributePointer>& getOutputs() const { return _outputs; }
/// Returns a reference to the level of detail that will determine subdivision levels.
const MetavoxelLOD& getLOD() const { return _lod; }
void setLOD(const MetavoxelLOD& lod) { _lod = lod; }
float getMinimumLODThresholdMultiplier() const { return _minimumLODThresholdMultiplier; }
/// Prepares for a new tour of the metavoxel data.
virtual void prepare();
/// Visits a metavoxel.
/// \param info the metavoxel data
/// \return if true, continue descending; if false, stop
virtual bool visit(MetavoxelInfo& info) = 0;
/// \return the encoded order in which to traverse the children, zero to stop recursion, or -1 to short-circuit the tour
virtual int visit(MetavoxelInfo& info) = 0;
protected:
QVector<AttributePointer> _inputs;
QVector<AttributePointer> _outputs;
MetavoxelLOD _lod;
float _minimumLODThresholdMultiplier;
};
typedef QSharedPointer<MetavoxelVisitor> MetavoxelVisitorPointer;
/// Interface for visitors to spanners.
/// Base class for visitors to spanners.
class SpannerVisitor : public MetavoxelVisitor {
public:
SpannerVisitor(const QVector<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>());
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD());
/// Visits a spanner.
virtual void visit(Spanner* spanner) = 0;
/// \return true to continue, false to short-circuit the tour
virtual bool visit(Spanner* spanner) = 0;
virtual void prepare();
virtual bool visit(MetavoxelInfo& info);
virtual int visit(MetavoxelInfo& info);
protected:
int _spannerInputCount;
};
/// Base class for ray intersection visitors.
class RayIntersectionVisitor : public MetavoxelVisitor {
public:
RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction,
const QVector<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD());
/// Visits a metavoxel that the ray intersects.
virtual int visit(MetavoxelInfo& info, float distance) = 0;
virtual int visit(MetavoxelInfo& info);
protected:
glm::vec3 _origin;
glm::vec3 _direction;
int _order;
};
/// Base class for ray intersection spanner visitors.
class RaySpannerIntersectionVisitor : public RayIntersectionVisitor {
public:
RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction,
const QVector<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD());
/// Visits a spanner that the ray intersects.
/// \return true to continue, false to short-circuit the tour
virtual bool visit(Spanner* spanner, float distance) = 0;
virtual void prepare();
virtual int visit(MetavoxelInfo& info, float distance);
protected:
@ -191,7 +319,8 @@ class MetavoxelGuide : public SharedObject {
public:
/// Guides the specified visitor to the contained voxels.
virtual void guide(MetavoxelVisitation& visitation) = 0;
/// \return true to keep going, false to short circuit the tour
virtual bool guide(MetavoxelVisitation& visitation) = 0;
};
/// Guides visitors through the explicit content of the system.
@ -202,7 +331,7 @@ public:
Q_INVOKABLE DefaultMetavoxelGuide();
virtual void guide(MetavoxelVisitation& visitation);
virtual bool guide(MetavoxelVisitation& visitation);
};
/// A temporary test guide that just makes the existing voxels throb with delight.
@ -214,7 +343,7 @@ public:
Q_INVOKABLE ThrobbingMetavoxelGuide();
virtual void guide(MetavoxelVisitation& visitation);
virtual bool guide(MetavoxelVisitation& visitation);
private:
@ -230,7 +359,7 @@ public:
Q_INVOKABLE ScriptedMetavoxelGuide();
virtual void guide(MetavoxelVisitation& visitation);
virtual bool guide(MetavoxelVisitation& visitation);
public slots:
@ -294,6 +423,13 @@ public:
void setGranularity(float granularity) { _granularity = granularity; }
float getGranularity() const { return _granularity; }
/// Returns a reference to the list of attributes associated with this spanner.
virtual const QVector<AttributePointer>& getAttributes() const;
/// Sets the attribute values associated with this spanner in the supplied info.
/// \return true to recurse, false to stop
virtual bool getAttributeValues(MetavoxelInfo& info) const;
/// Checks whether we've visited this object on the current traversal. If we have, returns false.
/// If we haven't, sets the last visit identifier and returns true.
bool testAndSetVisited();
@ -301,6 +437,9 @@ public:
/// Returns a pointer to the renderer, creating it if necessary.
SpannerRenderer* getRenderer();
/// Finds the intersection between the described ray and this spanner.
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
signals:
void boundsWillChange();
@ -308,6 +447,8 @@ signals:
protected:
SpannerRenderer* _renderer;
/// Returns the name of the class to instantiate in order to render this spanner.
virtual QByteArray getRendererClassName() const;
@ -316,7 +457,6 @@ private:
Box _bounds;
float _granularity;
int _lastVisit; ///< the identifier of the last visit
SpannerRenderer* _renderer;
static int _visit; ///< the global visit counter
};
@ -332,6 +472,7 @@ public:
virtual void init(Spanner* spanner);
virtual void simulate(float deltaTime);
virtual void render(float alpha);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
};
/// An object with a 3D transform.
@ -363,10 +504,45 @@ signals:
private:
glm::vec3 _translation;
glm::vec3 _rotation;
glm::vec3 _rotation; // Euler Angles in degrees
float _scale;
};
/// A sphere.
class Sphere : public Transformable {
Q_OBJECT
Q_PROPERTY(QColor color MEMBER _color WRITE setColor NOTIFY colorChanged)
public:
Q_INVOKABLE Sphere();
void setColor(const QColor& color);
const QColor& getColor() const { return _color; }
virtual const QVector<AttributePointer>& getAttributes() const;
virtual bool getAttributeValues(MetavoxelInfo& info) const;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
signals:
void colorChanged(const QColor& color);
protected:
virtual QByteArray getRendererClassName() const;
private slots:
void updateBounds();
private:
void getNormal(MetavoxelInfo& info) const;
QColor _color;
};
/// A static 3D model loaded from the network.
class StaticModel : public Transformable {
Q_OBJECT
@ -379,6 +555,8 @@ public:
void setURL(const QUrl& url);
const QUrl& getURL() const { return _url; }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
signals:
void urlChanged(const QUrl& url);

View file

@ -6,11 +6,10 @@
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
//
#include "MetavoxelData.h"
#include "MetavoxelMessages.h"
void MetavoxelEditMessage::apply(MetavoxelData& data) const {
static_cast<const MetavoxelEdit*>(edit.data())->apply(data);
void MetavoxelEditMessage::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
static_cast<const MetavoxelEdit*>(edit.data())->apply(data, objects);
}
MetavoxelEdit::~MetavoxelEdit() {
@ -25,7 +24,7 @@ public:
BoxSetEditVisitor(const BoxSetEdit& edit);
virtual bool visit(MetavoxelInfo& info);
virtual int visit(MetavoxelInfo& info);
private:
@ -37,29 +36,29 @@ BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) :
_edit(edit) {
}
bool BoxSetEditVisitor::visit(MetavoxelInfo& info) {
int BoxSetEditVisitor::visit(MetavoxelInfo& info) {
// find the intersection between volume and voxel
glm::vec3 minimum = glm::max(info.minimum, _edit.region.minimum);
glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _edit.region.maximum);
glm::vec3 size = maximum - minimum;
if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) {
return false; // disjoint
return STOP_RECURSION; // disjoint
}
float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size);
if (volume >= 1.0f) {
info.outputValues[0] = _edit.value;
return false; // entirely contained
return STOP_RECURSION; // entirely contained
}
if (info.size <= _edit.granularity) {
if (volume >= 0.5f) {
info.outputValues[0] = _edit.value;
}
return false; // reached granularity limit; take best guess
return STOP_RECURSION; // reached granularity limit; take best guess
}
return true; // subdivide
return DEFAULT_ORDER; // subdivide
}
void BoxSetEdit::apply(MetavoxelData& data) const {
void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
while (!data.getBounds().contains(region)) {
data.expand();
@ -78,7 +77,7 @@ public:
GlobalSetEditVisitor(const GlobalSetEdit& edit);
virtual bool visit(MetavoxelInfo& info);
virtual int visit(MetavoxelInfo& info);
private:
@ -90,12 +89,12 @@ GlobalSetEditVisitor::GlobalSetEditVisitor(const GlobalSetEdit& edit) :
_edit(edit) {
}
bool GlobalSetEditVisitor::visit(MetavoxelInfo& info) {
int GlobalSetEditVisitor::visit(MetavoxelInfo& info) {
info.outputValues[0] = _edit.value;
return false; // entirely contained
return STOP_RECURSION; // entirely contained
}
void GlobalSetEdit::apply(MetavoxelData& data) const {
void GlobalSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
GlobalSetEditVisitor visitor(*this);
data.guide(visitor);
}
@ -105,9 +104,8 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh
spanner(spanner) {
}
void InsertSpannerEdit::apply(MetavoxelData& data) const {
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
data.insert(attribute, spanner->getBounds(), spanner->getGranularity(), this->spanner);
void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
data.insert(attribute, spanner);
}
RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) :
@ -115,13 +113,56 @@ RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id)
id(id) {
}
void RemoveSpannerEdit::apply(MetavoxelData& data) const {
void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
SharedObject* object = objects.value(id);
if (!object) {
qDebug() << "Missing object to remove" << id;
return;
}
data.remove(attribute, object);
}
ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) :
attribute(attribute) {
}
void ClearSpannersEdit::apply(MetavoxelData& data) const {
void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
data.clear(attribute);
}
class SetSpannerEditVisitor : public MetavoxelVisitor {
public:
SetSpannerEditVisitor(Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
};
SetSpannerEditVisitor::SetSpannerEditVisitor(Spanner* spanner) :
MetavoxelVisitor(QVector<AttributePointer>(), spanner->getAttributes()),
_spanner(spanner) {
}
int SetSpannerEditVisitor::visit(MetavoxelInfo& info) {
return _spanner->getAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION;
}
SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) :
spanner(spanner) {
}
void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
// expand to fit the entire spanner
while (!data.getBounds().contains(spanner->getBounds())) {
data.expand();
}
SetSpannerEditVisitor visitor(spanner);
data.guide(visitor);
}

View file

@ -9,10 +9,7 @@
#ifndef __interface__MetavoxelMessages__
#define __interface__MetavoxelMessages__
#include "AttributeRegistry.h"
#include "MetavoxelUtil.h"
class MetavoxelData;
#include "MetavoxelData.h"
/// Requests to close the session.
class CloseSessionMessage {
@ -49,7 +46,7 @@ class ClientStateMessage {
public:
STREAM glm::vec3 position;
STREAM MetavoxelLOD lod;
};
DECLARE_STREAMABLE_METATYPE(ClientStateMessage)
@ -69,7 +66,7 @@ public:
STREAM QVariant edit;
void apply(MetavoxelData& data) const;
void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(MetavoxelEditMessage)
@ -80,7 +77,7 @@ public:
virtual ~MetavoxelEdit();
virtual void apply(MetavoxelData& data) const = 0;
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const = 0;
};
/// An edit that sets the region within a box to a value.
@ -96,7 +93,7 @@ public:
BoxSetEdit(const Box& region = Box(), float granularity = 0.0f,
const OwnedAttributeValue& value = OwnedAttributeValue());
virtual void apply(MetavoxelData& data) const;
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(BoxSetEdit)
@ -111,7 +108,7 @@ public:
GlobalSetEdit(const OwnedAttributeValue& value = OwnedAttributeValue());
virtual void apply(MetavoxelData& data) const;
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(GlobalSetEdit)
@ -128,7 +125,7 @@ public:
InsertSpannerEdit(const AttributePointer& attribute = AttributePointer(),
const SharedObjectPointer& spanner = SharedObjectPointer());
virtual void apply(MetavoxelData& data) const;
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(InsertSpannerEdit)
@ -144,7 +141,7 @@ public:
RemoveSpannerEdit(const AttributePointer& attribute = AttributePointer(), int id = 0);
virtual void apply(MetavoxelData& data) const;
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(RemoveSpannerEdit)
@ -159,9 +156,24 @@ public:
ClearSpannersEdit(const AttributePointer& attribute = AttributePointer());
virtual void apply(MetavoxelData& data) const;
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(ClearSpannersEdit)
/// An edit that sets a spanner's attributes in the voxel tree.
class SetSpannerEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM SharedObjectPointer spanner;
SetSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(SetSpannerEdit)
#endif /* defined(__interface__MetavoxelMessages__) */

View file

@ -150,6 +150,12 @@ Box::Box(const glm::vec3& minimum, const glm::vec3& maximum) :
minimum(minimum), maximum(maximum) {
}
bool Box::contains(const glm::vec3& point) const {
return point.x >= minimum.x && point.x <= maximum.x &&
point.y >= minimum.y && point.y <= maximum.y &&
point.z >= minimum.z && point.z <= maximum.z;
}
bool Box::contains(const Box& other) const {
return other.minimum.x >= minimum.x && other.maximum.x <= maximum.x &&
other.minimum.y >= minimum.y && other.maximum.y <= maximum.y &&
@ -162,6 +168,137 @@ bool Box::intersects(const Box& other) const {
other.maximum.z >= minimum.z && other.minimum.z <= maximum.z;
}
const int X_MAXIMUM_FLAG = 1;
const int Y_MAXIMUM_FLAG = 2;
const int Z_MAXIMUM_FLAG = 4;
glm::vec3 Box::getVertex(int index) const {
return glm::vec3(
(index & X_MAXIMUM_FLAG) ? maximum.x : minimum.x,
(index & Y_MAXIMUM_FLAG) ? maximum.y : minimum.y,
(index & Z_MAXIMUM_FLAG) ? maximum.z : minimum.z);
}
// finds the intersection between a ray and the facing plane on one axis
static bool findIntersection(float origin, float direction, float minimum, float maximum, float& distance) {
if (direction > EPSILON) {
distance = (minimum - origin) / direction;
return true;
} else if (direction < -EPSILON) {
distance = (maximum - origin) / direction;
return true;
}
return false;
}
// determines whether a value is within the extents
static bool isWithin(float value, float minimum, float maximum) {
return value >= minimum && value <= maximum;
}
bool Box::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
// handle the trivial case where the box contains the origin
if (contains(origin)) {
distance = 0.0f;
return true;
}
// check each axis
float axisDistance;
if ((findIntersection(origin.x, direction.x, minimum.x, maximum.x, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, minimum.y, maximum.y) &&
isWithin(origin.z + axisDistance*direction.z, minimum.z, maximum.z))) {
distance = axisDistance;
return true;
}
if ((findIntersection(origin.y, direction.y, minimum.y, maximum.y, axisDistance) && axisDistance >= 0 &&
isWithin(origin.x + axisDistance*direction.x, minimum.x, maximum.x) &&
isWithin(origin.z + axisDistance*direction.z, minimum.z, maximum.z))) {
distance = axisDistance;
return true;
}
if ((findIntersection(origin.z, direction.z, minimum.z, maximum.z, axisDistance) && axisDistance >= 0 &&
isWithin(origin.y + axisDistance*direction.y, minimum.y, maximum.y) &&
isWithin(origin.x + axisDistance*direction.x, minimum.x, maximum.x))) {
distance = axisDistance;
return true;
}
return false;
}
Box operator*(const glm::mat4& matrix, const Box& box) {
// start with the constant component
Box newBox(glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]), glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]));
// for each element, we choose the minimum or maximum based on the matrix sign
if (matrix[0][0] >= 0.0f) {
newBox.minimum.x += matrix[0][0] * box.minimum.x;
newBox.maximum.x += matrix[0][0] * box.maximum.x;
} else {
newBox.minimum.x += matrix[0][0] * box.maximum.x;
newBox.maximum.x += matrix[0][0] * box.minimum.x;
}
if (matrix[1][0] >= 0.0f) {
newBox.minimum.x += matrix[1][0] * box.minimum.y;
newBox.maximum.x += matrix[1][0] * box.maximum.y;
} else {
newBox.minimum.x += matrix[1][0] * box.maximum.y;
newBox.maximum.x += matrix[1][0] * box.minimum.y;
}
if (matrix[2][0] >= 0.0f) {
newBox.minimum.x += matrix[2][0] * box.minimum.z;
newBox.maximum.x += matrix[2][0] * box.maximum.z;
} else {
newBox.minimum.x += matrix[2][0] * box.maximum.z;
newBox.maximum.x += matrix[2][0] * box.minimum.z;
}
if (matrix[0][1] >= 0.0f) {
newBox.minimum.y += matrix[0][1] * box.minimum.x;
newBox.maximum.y += matrix[0][1] * box.maximum.x;
} else {
newBox.minimum.y += matrix[0][1] * box.maximum.x;
newBox.maximum.y += matrix[0][1] * box.minimum.x;
}
if (matrix[1][1] >= 0.0f) {
newBox.minimum.y += matrix[1][1] * box.minimum.y;
newBox.maximum.y += matrix[1][1] * box.maximum.y;
} else {
newBox.minimum.y += matrix[1][1] * box.maximum.y;
newBox.maximum.y += matrix[1][1] * box.minimum.y;
}
if (matrix[2][1] >= 0.0f) {
newBox.minimum.y += matrix[2][1] * box.minimum.z;
newBox.maximum.y += matrix[2][1] * box.maximum.z;
} else {
newBox.minimum.y += matrix[2][1] * box.maximum.z;
newBox.maximum.y += matrix[2][1] * box.minimum.z;
}
if (matrix[0][2] >= 0.0f) {
newBox.minimum.z += matrix[0][2] * box.minimum.x;
newBox.maximum.z += matrix[0][2] * box.maximum.x;
} else {
newBox.minimum.z += matrix[0][2] * box.maximum.x;
newBox.maximum.z += matrix[0][2] * box.minimum.x;
}
if (matrix[1][2] >= 0.0f) {
newBox.minimum.z += matrix[1][2] * box.minimum.y;
newBox.maximum.z += matrix[1][2] * box.maximum.y;
} else {
newBox.minimum.z += matrix[1][2] * box.maximum.y;
newBox.maximum.z += matrix[1][2] * box.minimum.y;
}
if (matrix[2][2] >= 0.0f) {
newBox.minimum.z += matrix[2][2] * box.minimum.z;
newBox.maximum.z += matrix[2][2] * box.maximum.z;
} else {
newBox.minimum.z += matrix[2][2] * box.maximum.z;
newBox.maximum.z += matrix[2][2] * box.minimum.z;
}
return newBox;
}
QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) {
QVBoxLayout* layout = new QVBoxLayout();
layout->setContentsMargins(QMargins());

View file

@ -34,20 +34,32 @@ class Box {
public:
static const int VERTEX_COUNT = 8;
STREAM glm::vec3 minimum;
STREAM glm::vec3 maximum;
Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3());
bool contains(const glm::vec3& point) const;
bool contains(const Box& other) const;
bool intersects(const Box& other) const;
float getLongestSide() const { return qMax(qMax(maximum.x - minimum.x, maximum.y - minimum.y), maximum.z - minimum.z); }
glm::vec3 getVertex(int index) const;
glm::vec3 getCenter() const { return (minimum + maximum) * 0.5f; }
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
};
DECLARE_STREAMABLE_METATYPE(Box)
Box operator*(const glm::mat4& matrix, const Box& box);
/// Editor for meta-object values.
class QMetaObjectEditor : public QWidget {
Q_OBJECT

View file

@ -18,7 +18,12 @@
REGISTER_META_OBJECT(SharedObject)
SharedObject::SharedObject() : _id(++_lastID), _referenceCount(0) {
SharedObject::SharedObject() :
_id(++_lastID),
_remoteID(0),
_referenceCount(0) {
_weakHash.insert(_id, this);
}
void SharedObject::incrementReferenceCount() {
@ -27,10 +32,8 @@ void SharedObject::incrementReferenceCount() {
void SharedObject::decrementReferenceCount() {
if (--_referenceCount == 0) {
_weakHash.remove(_id);
delete this;
} else if (_referenceCount == 1) {
emit referenceCountDroppedToOne();
}
}
@ -51,6 +54,12 @@ SharedObject* SharedObject::clone() const {
}
bool SharedObject::equals(const SharedObject* other) const {
if (!other) {
return false;
}
if (other == this) {
return true;
}
// default behavior is to compare the properties
const QMetaObject* metaObject = this->metaObject();
if (metaObject != other->metaObject()) {
@ -83,6 +92,17 @@ void SharedObject::dump(QDebug debug) const {
}
int SharedObject::_lastID = 0;
WeakSharedObjectHash SharedObject::_weakHash;
void pruneWeakSharedObjectHash(WeakSharedObjectHash& hash) {
for (WeakSharedObjectHash::iterator it = hash.begin(); it != hash.end(); ) {
if (!it.value()) {
it = hash.erase(it);
} else {
it++;
}
}
}
SharedObjectEditor::SharedObjectEditor(const QMetaObject* metaObject, bool nullable, QWidget* parent) : QWidget(parent) {
QVBoxLayout* layout = new QVBoxLayout();

View file

@ -9,23 +9,38 @@
#ifndef __interface__SharedObject__
#define __interface__SharedObject__
#include <QHash>
#include <QMetaType>
#include <QObject>
#include <QPointer>
#include <QSet>
#include <QWidget>
#include <QtDebug>
class QComboBox;
class SharedObject;
typedef QHash<int, QPointer<SharedObject> > WeakSharedObjectHash;
/// A QObject that may be shared over the network.
class SharedObject : public QObject {
Q_OBJECT
public:
/// Returns the weak hash under which all local shared objects are registered.
static const WeakSharedObjectHash& getWeakHash() { return _weakHash; }
Q_INVOKABLE SharedObject();
int getID() { return _id; }
/// Returns the unique local ID for this object.
int getID() const { return _id; }
/// Returns the unique remote ID for this object, or zero if this is a local object.
int getRemoteID() const { return _remoteID; }
void setRemoteID(int remoteID) { _remoteID = remoteID; }
int getReferenceCount() const { return _referenceCount; }
void incrementReferenceCount();
@ -40,19 +55,19 @@ public:
// Dumps the contents of this object to the debug output.
virtual void dump(QDebug debug = QDebug(QtDebugMsg)) const;
signals:
/// Emitted when the reference count drops to one.
void referenceCountDroppedToOne();
private:
int _id;
int _remoteID;
int _referenceCount;
static int _lastID;
static WeakSharedObjectHash _weakHash;
};
/// Removes the null references from the supplied hash.
void pruneWeakSharedObjectHash(WeakSharedObjectHash& hash);
/// A pointer to a shared object.
template<class T> class SharedObjectPointerTemplate {
public:
@ -153,6 +168,11 @@ template<class T> uint qHash(const SharedObjectPointerTemplate<T>& pointer, uint
return qHash(pointer.data(), seed);
}
template<class T, class X> bool equals(const SharedObjectPointerTemplate<T>& first,
const SharedObjectPointerTemplate<X>& second) {
return first ? first->equals(second) : !second;
}
typedef SharedObjectPointerTemplate<SharedObject> SharedObjectPointer;
Q_DECLARE_METATYPE(SharedObjectPointer)

View file

@ -19,8 +19,6 @@ JurisdictionListener::JurisdictionListener(NodeType_t type) :
_nodeType(type),
_packetSender(JurisdictionListener::DEFAULT_PACKETS_PER_SECOND)
{
ReceivedPacketProcessor::_dontSleep = true; // we handle sleeping so this class doesn't need to
connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &JurisdictionListener::nodeKilled);
// tell our NodeList we want to hear about nodes with our node type

View file

@ -384,14 +384,14 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const
// Compute the angular distance between the two orientations
const float ORIENTATION_SIMILAR_ENOUGH = 10.0f; // 10 degrees in any direction
glm::quat dQOrientation = _orientation * glm::inverse(compareTo._orientation);
float angleOrientation = compareTo._orientation == _orientation ? 0.0f : glm::angle(dQOrientation);
float angleOrientation = compareTo._orientation == _orientation ? 0.0f : glm::degrees(glm::angle(dQOrientation));
if (isNaN(angleOrientation)) {
angleOrientation = 0.0f;
}
glm::quat dQEyeOffsetOrientation = _eyeOffsetOrientation * glm::inverse(compareTo._eyeOffsetOrientation);
float angleEyeOffsetOrientation = compareTo._eyeOffsetOrientation == _eyeOffsetOrientation
? 0.0f : glm::angle(dQEyeOffsetOrientation);
? 0.0f : glm::degrees(glm::angle(dQEyeOffsetOrientation));
if (isNaN(angleEyeOffsetOrientation)) {
angleOrientation = 0.0f;
}
@ -463,7 +463,7 @@ void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3&
void ViewFrustum::computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearValue, float& farValue,
glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const {
// compute our dimensions the usual way
float hheight = _nearClip * tanf(_fieldOfView * 0.5f * PI_OVER_180);
float hheight = _nearClip * tanf(_fieldOfView * 0.5f * RADIANS_PER_DEGREE);
float hwidth = _aspectRatio * hheight;
// get our frustum corners in view space

View file

@ -120,7 +120,7 @@ private:
glm::vec3 _right;
// Lens attributes
float _fieldOfView;
float _fieldOfView; // degrees
float _aspectRatio;
float _nearClip;
float _farClip;

View file

@ -387,11 +387,10 @@ TouchEvent::TouchEvent(const QTouchEvent& event, const TouchEvent& other) {
calculateMetaAttributes(other);
}
// returns the degrees between two points (note: 0 degrees is 'east')
// returns the angle (in degrees) between two points (note: 0 degrees is 'east')
float angleBetweenPoints(const glm::vec2& a, const glm::vec2& b ) {
glm::vec2 length = b - a;
float radian = std::atan2(length.y, length.x);
float angle = radian * 180.0f / PIE;
float angle = DEGREES_PER_RADIAN * std::atan2(length.y, length.x);
if (angle < 0) {
angle += 360.0f;
};

View file

@ -74,6 +74,8 @@ public:
float radius;
bool isPinching;
bool isPinchOpening;
// angles are in degrees
QVector<float> angles; // angle from center to each point
float angle; // the average of the angles
float deltaAngle; // the change in average angle from last event

View file

@ -22,8 +22,8 @@ glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) {
return q1 * q2;
}
glm::quat Quat::fromVec3(const glm::vec3& vec3) {
return glm::quat(vec3);
glm::quat Quat::fromVec3(const glm::vec3& eulerAngles) {
return glm::quat(glm::radians(eulerAngles));
}
glm::quat Quat::fromPitchYawRoll(float pitch, float yaw, float roll) {
@ -47,11 +47,11 @@ glm::vec3 Quat::getUp(const glm::quat& orientation) {
}
glm::vec3 Quat::safeEulerAngles(const glm::quat& orientation) {
return ::safeEulerAngles(orientation);
return glm::degrees(::safeEulerAngles(orientation));
}
glm::quat Quat::angleAxis(float angle, const glm::vec3& v) {
return glm::angleAxis(angle, v);
return glm::angleAxis(glm::radians(angle), v);
}
glm::quat Quat::mix(const glm::quat& q1, const glm::quat& q2, float alpha) {

View file

@ -24,13 +24,13 @@ class Quat : public QObject {
public slots:
glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
glm::quat fromVec3(const glm::vec3& vec3);
glm::quat fromPitchYawRoll(float pitch, float yaw, float roll);
glm::quat fromPitchYawRoll(float pitch, float yaw, float roll); // degrees
glm::quat inverse(const glm::quat& q);
glm::vec3 getFront(const glm::quat& orientation);
glm::vec3 getRight(const glm::quat& orientation);
glm::vec3 getUp(const glm::quat& orientation);
glm::vec3 safeEulerAngles(const glm::quat& orientation);
glm::quat angleAxis(float angle, const glm::vec3& v);
glm::vec3 safeEulerAngles(const glm::quat& orientation); // degrees
glm::quat angleAxis(float angle, const glm::vec3& v); // degrees
glm::quat mix(const glm::quat& q1, const glm::quat& q2, float alpha);
void print(const QString& lable, const glm::quat& q);
};

View file

@ -45,6 +45,8 @@ void GenericThread::terminate() {
if (_isThreaded) {
_stopThread = true;
terminating();
if (_thread) {
_thread->wait();
_thread->deleteLater();

View file

@ -33,6 +33,8 @@ public:
/// Override this function to do whatever your class actually does, return false to exit thread early.
virtual bool process() = 0;
virtual void terminating() { }; // lets your subclass know we're terminating, and it should respond appropriately
bool isThreaded() const { return _isThreaded; }
public slots:

View file

@ -54,6 +54,9 @@ void PacketSender::queuePacketForSending(const SharedNodePointer& destinationNod
unlock();
_totalPacketsQueued++;
_totalBytesQueued += packet.size();
// Make sure to wake our actual processing thread because we now have packets for it to process.
_hasPackets.wakeAll();
}
void PacketSender::setPacketsPerSecond(int packetsPerSecond) {
@ -68,6 +71,9 @@ bool PacketSender::process() {
return nonThreadedProcess();
}
void PacketSender::terminating() {
_hasPackets.wakeAll();
}
bool PacketSender::threadedProcess() {
bool hasSlept = false;
@ -109,11 +115,12 @@ bool PacketSender::threadedProcess() {
}
}
// if threaded and we haven't slept? We want to sleep a little so we don't hog the CPU, but
// we don't want to sleep too long because how ever much we sleep will delay any future unsent
// packets that arrive while we're sleeping. So we sleep 1/2 of our target fps interval
// if threaded and we haven't slept? We want to wait for our consumer to signal us with new packets
if (!hasSlept) {
usleep(MINIMAL_SLEEP_INTERVAL);
// wait till we have packets
_waitingOnPacketsMutex.lock();
_hasPackets.wait(&_waitingOnPacketsMutex);
_waitingOnPacketsMutex.unlock();
}
return isStillRunning();

View file

@ -11,6 +11,8 @@
#ifndef __shared__PacketSender__
#define __shared__PacketSender__
#include <QWaitCondition>
#include "GenericThread.h"
#include "NetworkPacket.h"
#include "NodeList.h"
@ -44,6 +46,7 @@ public:
int getPacketsPerSecond() const { return _packetsPerSecond; }
virtual bool process();
virtual void terminating();
/// are there packets waiting in the send queue to be sent
bool hasPacketsToSend() const { return _packets.size() > 0; }
@ -113,6 +116,9 @@ private:
quint64 _totalPacketsQueued;
quint64 _totalBytesQueued;
QWaitCondition _hasPackets;
QMutex _waitingOnPacketsMutex;
};
#endif // __shared__PacketSender__

View file

@ -12,8 +12,8 @@
#include "ReceivedPacketProcessor.h"
#include "SharedUtil.h"
ReceivedPacketProcessor::ReceivedPacketProcessor() {
_dontSleep = false;
void ReceivedPacketProcessor::terminating() {
_hasPackets.wakeAll();
}
void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& destinationNode, const QByteArray& packet) {
@ -24,18 +24,19 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& desti
lock();
_packets.push_back(networkPacket);
unlock();
// Make sure to wake our actual processing thread because we now have packets for it to process.
_hasPackets.wakeAll();
}
bool ReceivedPacketProcessor::process() {
// If a derived class handles process sleeping, like the JurisdiciontListener, then it can set
// this _dontSleep member and we will honor that request.
if (_packets.size() == 0 && !_dontSleep) {
const quint64 RECEIVED_THREAD_SLEEP_INTERVAL = (1000 * 1000)/60; // check at 60fps
usleep(RECEIVED_THREAD_SLEEP_INTERVAL);
if (_packets.size() == 0) {
_waitingOnPacketsMutex.lock();
_hasPackets.wait(&_waitingOnPacketsMutex);
_waitingOnPacketsMutex.unlock();
}
while (_packets.size() > 0) {
lock(); // lock to make sure nothing changes on us
NetworkPacket& packet = _packets.front(); // get the oldest packet
NetworkPacket temporary = packet; // make a copy of the packet in case the vector is resized on us

View file

@ -11,13 +11,15 @@
#ifndef __shared__ReceivedPacketProcessor__
#define __shared__ReceivedPacketProcessor__
#include <QWaitCondition>
#include "GenericThread.h"
#include "NetworkPacket.h"
/// Generalized threaded processor for handling received inbound packets.
class ReceivedPacketProcessor : public GenericThread {
public:
ReceivedPacketProcessor();
ReceivedPacketProcessor() { }
/// Add packet from network receive thread to the processing queue.
/// \param sockaddr& senderAddress the address of the sender
@ -43,11 +45,13 @@ protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
bool _dontSleep;
virtual void terminating();
private:
std::vector<NetworkPacket> _packets;
QWaitCondition _hasPackets;
QMutex _waitingOnPacketsMutex;
};
#endif // __shared__PacketReceiver__

View file

@ -107,7 +107,7 @@ Resource::Resource(const QUrl& url, bool delayLoad) :
_reply(NULL),
_attempts(0) {
if (!url.isValid()) {
if (!(url.isValid() && ResourceCache::getNetworkAccessManager())) {
_startedLoading = _failedToLoad = true;
return;
}

View file

@ -501,27 +501,27 @@ int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm
}
int packFloatAngleToTwoByte(unsigned char* buffer, float angle) {
const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.0);
int packFloatAngleToTwoByte(unsigned char* buffer, float degrees) {
const float ANGLE_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 360.f);
uint16_t angleHolder = floorf((angle + 180) * ANGLE_CONVERSION_RATIO);
uint16_t angleHolder = floorf((degrees + 180.f) * ANGLE_CONVERSION_RATIO);
memcpy(buffer, &angleHolder, sizeof(uint16_t));
return sizeof(uint16_t);
}
int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer) {
*destinationPointer = (*byteAnglePointer / (float) std::numeric_limits<uint16_t>::max()) * 360.0 - 180;
*destinationPointer = (*byteAnglePointer / (float) std::numeric_limits<uint16_t>::max()) * 360.f - 180.f;
return sizeof(uint16_t);
}
int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput) {
const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 2.0);
const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits<uint16_t>::max() / 2.f);
uint16_t quatParts[4];
quatParts[0] = floorf((quatInput.x + 1.0) * QUAT_PART_CONVERSION_RATIO);
quatParts[1] = floorf((quatInput.y + 1.0) * QUAT_PART_CONVERSION_RATIO);
quatParts[2] = floorf((quatInput.z + 1.0) * QUAT_PART_CONVERSION_RATIO);
quatParts[3] = floorf((quatInput.w + 1.0) * QUAT_PART_CONVERSION_RATIO);
quatParts[0] = floorf((quatInput.x + 1.f) * QUAT_PART_CONVERSION_RATIO);
quatParts[1] = floorf((quatInput.y + 1.f) * QUAT_PART_CONVERSION_RATIO);
quatParts[2] = floorf((quatInput.z + 1.f) * QUAT_PART_CONVERSION_RATIO);
quatParts[3] = floorf((quatInput.w + 1.f) * QUAT_PART_CONVERSION_RATIO);
memcpy(buffer, &quatParts, sizeof(quatParts));
return sizeof(quatParts);
@ -531,16 +531,16 @@ int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatO
uint16_t quatParts[4];
memcpy(&quatParts, buffer, sizeof(quatParts));
quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.0) - 1.0;
quatOutput.x = ((quatParts[0] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
quatOutput.y = ((quatParts[1] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
quatOutput.z = ((quatParts[2] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
quatOutput.w = ((quatParts[3] / (float) std::numeric_limits<uint16_t>::max()) * 2.f) - 1.f;
return sizeof(quatParts);
}
float SMALL_LIMIT = 10.0;
float LARGE_LIMIT = 1000.0;
float SMALL_LIMIT = 10.f;
float LARGE_LIMIT = 1000.f;
int packFloatRatioToTwoByte(unsigned char* buffer, float ratio) {
// if the ratio is less than 10, then encode it as a positive number scaled from 0 to int16::max()
@ -642,24 +642,24 @@ glm::vec3 safeEulerAngles(const glm::quat& q) {
float sy = 2.0f * (q.y * q.w - q.x * q.z);
if (sy < 1.0f - EPSILON) {
if (sy > -1.0f + EPSILON) {
return glm::degrees(glm::vec3(
return 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))));
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::degrees(glm::vec3(
return glm::vec3(
0.0f,
PIf * -0.5f,
atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
- 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::degrees(glm::vec3(
return glm::vec3(
0.0f,
PIf * 0.5f,
-atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z))));
PI_OVER_TWO,
-atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
}
}

View file

@ -47,9 +47,13 @@ static const float ZERO = 0.0f;
static const float ONE = 1.0f;
static const float ONE_HALF = 0.5f;
static const float ONE_THIRD = 0.333333f;
static const float PIE = 3.141592f;
static const float PI_TIMES_TWO = 3.141592f * 2.0f;
static const float PI_OVER_180 = 3.141592f / 180.0f;
static const float PI = 3.14159265358979f;
static const float TWO_PI = 2.f * PI;
static const float PI_OVER_TWO = ONE_HALF * PI;
static const float RADIANS_PER_DEGREE = PI / 180.0f;
static const float DEGREES_PER_RADIAN = 180.0f / PI;
static const float EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations
static const float SQUARE_ROOT_OF_2 = (float)sqrt(2.f);
static const float SQUARE_ROOT_OF_3 = (float)sqrt(3.f);
@ -133,8 +137,8 @@ bool isBetween(int64_t value, int64_t max, int64_t min);
// These pack/unpack functions are designed to start specific known types in as efficient a manner
// as possible. Taking advantage of the known characteristics of the semantic types.
// Angles are known to be between 0 and 360deg, this allows us to encode in 16bits with great accuracy
int packFloatAngleToTwoByte(unsigned char* buffer, float angle);
// Angles are known to be between 0 and 360 degrees, this allows us to encode in 16bits with great accuracy
int packFloatAngleToTwoByte(unsigned char* buffer, float degrees);
int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destinationPointer);
// Orientation Quats are known to have 4 normalized components be between -1.0 and 1.0
@ -164,10 +168,7 @@ int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, flo
int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix);
#ifndef PIf
#define PIf 3.14159265f
#endif
/// \return vec3 with euler angles in radians
glm::vec3 safeEulerAngles(const glm::quat& q);
#endif /* defined(__hifi__SharedUtil__) */

View file

@ -10,8 +10,13 @@
#include <SharedUtil.h>
#include <MetavoxelMessages.h>
#include "MetavoxelTests.h"
REGISTER_META_OBJECT(TestSharedObjectA)
REGISTER_META_OBJECT(TestSharedObjectB)
MetavoxelTests::MetavoxelTests(int& argc, char** argv) :
QCoreApplication(argc, argv) {
}
@ -26,6 +31,8 @@ static int reliableMessagesSent = 0;
static int reliableMessagesReceived = 0;
static int streamedBytesSent = 0;
static int streamedBytesReceived = 0;
static int sharedObjectsCreated = 0;
static int sharedObjectsDestroyed = 0;
bool MetavoxelTests::run() {
@ -54,6 +61,7 @@ bool MetavoxelTests::run() {
qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived;
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived;
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
qDebug() << "All tests passed!";
@ -74,6 +82,15 @@ static QByteArray createRandomBytes() {
return createRandomBytes(MIN_BYTES, MAX_BYTES);
}
static SharedObjectPointer createRandomSharedObject() {
switch (randIntInRange(0, 2)) {
case 0: return new TestSharedObjectA(randFloat());
case 1: return new TestSharedObjectB();
case 2:
default: return SharedObjectPointer();
}
}
Endpoint::Endpoint(const QByteArray& datagramHeader) :
_sequencer(new DatagramSequencer(datagramHeader, this)),
_highPriorityMessagesToSend(0.0f),
@ -110,7 +127,7 @@ static QVariant createRandomMessage() {
return QVariant::fromValue(message);
}
case 1: {
TestMessageB message = { createRandomBytes() };
TestMessageB message = { createRandomBytes(), createRandomSharedObject() };
return QVariant::fromValue(message);
}
case 2:
@ -132,10 +149,15 @@ static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMe
}
if (type == TestMessageA::Type) {
return firstMessage.value<TestMessageA>() == secondMessage.value<TestMessageA>();
} else if (type == TestMessageB::Type) {
return firstMessage.value<TestMessageB>() == secondMessage.value<TestMessageB>();
TestMessageB first = firstMessage.value<TestMessageB>();
TestMessageB second = secondMessage.value<TestMessageB>();
return first.foo == second.foo && equals(first.bar, second.bar);
} else if (type == TestMessageC::Type) {
return firstMessage.value<TestMessageC>() == secondMessage.value<TestMessageC>();
} else {
return firstMessage == secondMessage;
}
@ -225,6 +247,9 @@ void Endpoint::sendDatagram(const QByteArray& datagram) {
}
void Endpoint::handleHighPriorityMessage(const QVariant& message) {
if (message.userType() == ClearSharedObjectMessage::Type) {
return;
}
if (_other->_highPriorityMessagesSent.isEmpty()) {
throw QString("Received unsent/already sent high priority message.");
}
@ -254,6 +279,10 @@ void Endpoint::readMessage(Bitstream& in) {
}
void Endpoint::handleReliableMessage(const QVariant& message) {
if (message.userType() == ClearSharedObjectMessage::Type ||
message.userType() == ClearMainChannelSharedObjectMessage::Type) {
return;
}
if (_other->_reliableMessagesSent.isEmpty()) {
throw QString("Received unsent/already sent reliable message.");
}
@ -277,3 +306,26 @@ void Endpoint::readReliableChannel() {
}
streamedBytesReceived += bytes.size();
}
TestSharedObjectA::TestSharedObjectA(float foo) :
_foo(foo) {
sharedObjectsCreated++;
}
TestSharedObjectA::~TestSharedObjectA() {
sharedObjectsDestroyed++;
}
void TestSharedObjectA::setFoo(float foo) {
if (_foo != foo) {
emit fooChanged(_foo = foo);
}
}
TestSharedObjectB::TestSharedObjectB() {
sharedObjectsCreated++;
}
TestSharedObjectB::~TestSharedObjectB() {
sharedObjectsDestroyed++;
}

View file

@ -64,6 +64,38 @@ private:
CircularBuffer _dataStreamed;
};
/// A simple shared object.
class TestSharedObjectA : public SharedObject {
Q_OBJECT
Q_PROPERTY(float foo READ getFoo WRITE setFoo NOTIFY fooChanged)
public:
Q_INVOKABLE TestSharedObjectA(float foo = 0.0f);
virtual ~TestSharedObjectA();
void setFoo(float foo);
float getFoo() const { return _foo; }
signals:
void fooChanged(float foo);
private:
float _foo;
};
/// Another simple shared object.
class TestSharedObjectB : public SharedObject {
Q_OBJECT
public:
Q_INVOKABLE TestSharedObjectB();
virtual ~TestSharedObjectB();
};
/// A simple test message.
class TestMessageA {
STREAMABLE
@ -84,6 +116,7 @@ class TestMessageB {
public:
STREAM QByteArray foo;
STREAM SharedObjectPointer bar;
};
DECLARE_STREAMABLE_METATYPE(TestMessageB)

View file

@ -25,7 +25,7 @@ void CollisionInfoTests::rotateThenTranslate() {
collision._contactPoint = yAxis;
collision._addedVelocity = xAxis + yAxis + zAxis;
glm::quat rotation = glm::angleAxis(rightAngle, zAxis);
glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
float distance = 3.f;
glm::vec3 translation = distance * yAxis;
@ -64,7 +64,7 @@ void CollisionInfoTests::translateThenRotate() {
collision._contactPoint = yAxis;
collision._addedVelocity = xAxis + yAxis + zAxis;
glm::quat rotation = glm::angleAxis( -rightAngle, zAxis);
glm::quat rotation = glm::angleAxis( -PI_OVER_TWO, zAxis);
float distance = 3.f;
glm::vec3 translation = distance * yAxis;

View file

@ -18,8 +18,6 @@ const glm::vec3 xAxis(1.f, 0.f, 0.f);
const glm::vec3 yAxis(0.f, 1.f, 0.f);
const glm::vec3 zAxis(0.f, 0.f, 1.f);
const float rightAngle = 90.f; // degrees
std::ostream& operator<<(std::ostream& s, const glm::vec3& v);
std::ostream& operator<<(std::ostream& s, const glm::quat& q);
std::ostream& operator<<(std::ostream& s, const glm::mat4& m);

View file

@ -485,7 +485,7 @@ void ShapeColliderTests::capsuleMissesCapsule() {
}
// rotate B and move it to the side
glm::quat rotation = glm::angleAxis(rightAngle, zAxis);
glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
capsuleB.setRotation(rotation);
capsuleB.setPosition((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis);
if (ShapeCollider::shapeShape(&capsuleA, &capsuleB, collisions))
@ -566,7 +566,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() {
}
{ // rotate B and move it to the side
glm::quat rotation = glm::angleAxis(rightAngle, zAxis);
glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
capsuleB.setRotation(rotation);
capsuleB.setPosition((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis);
@ -590,7 +590,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() {
{ // again, but this time check collision details
float overlap = 0.1f;
glm::quat rotation = glm::angleAxis(rightAngle, zAxis);
glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
capsuleB.setRotation(rotation);
glm::vec3 positionB = ((totalRadius + capsuleB.getHalfHeight()) - overlap) * xAxis;
capsuleB.setPosition(positionB);
@ -657,7 +657,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() {
{ // collide cylinder wall against cylinder wall
float overlap = 0.137f;
float shift = 0.317f * halfHeightA;
glm::quat rotation = glm::angleAxis(rightAngle, zAxis);
glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis);
capsuleB.setRotation(rotation);
glm::vec3 positionB = (totalRadius - overlap) * zAxis + shift * yAxis;
capsuleB.setPosition(positionB);