mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 09:43:25 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into voxel_animation
This commit is contained in:
commit
0bcd629c8a
12 changed files with 216 additions and 231 deletions
|
@ -82,39 +82,39 @@ protected:
|
|||
};
|
||||
|
||||
void GLCanvas::initializeGL() {
|
||||
static_cast<Application*>(QCoreApplication::instance())->initializeGL();
|
||||
Application::getInstance()->initializeGL();
|
||||
}
|
||||
|
||||
void GLCanvas::paintGL() {
|
||||
static_cast<Application*>(QCoreApplication::instance())->paintGL();
|
||||
Application::getInstance()->paintGL();
|
||||
}
|
||||
|
||||
void GLCanvas::resizeGL(int width, int height) {
|
||||
static_cast<Application*>(QCoreApplication::instance())->resizeGL(width, height);
|
||||
Application::getInstance()->resizeGL(width, height);
|
||||
}
|
||||
|
||||
void GLCanvas::keyPressEvent(QKeyEvent* event) {
|
||||
static_cast<Application*>(QCoreApplication::instance())->keyPressEvent(event);
|
||||
Application::getInstance()->keyPressEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::keyReleaseEvent(QKeyEvent* event) {
|
||||
static_cast<Application*>(QCoreApplication::instance())->keyReleaseEvent(event);
|
||||
Application::getInstance()->keyReleaseEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mouseMoveEvent(QMouseEvent* event) {
|
||||
static_cast<Application*>(QCoreApplication::instance())->mouseMoveEvent(event);
|
||||
Application::getInstance()->mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mousePressEvent(QMouseEvent* event) {
|
||||
static_cast<Application*>(QCoreApplication::instance())->mousePressEvent(event);
|
||||
Application::getInstance()->mousePressEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::mouseReleaseEvent(QMouseEvent* event) {
|
||||
static_cast<Application*>(QCoreApplication::instance())->mouseReleaseEvent(event);
|
||||
Application::getInstance()->mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void GLCanvas::wheelEvent(QWheelEvent* event) {
|
||||
static_cast<Application*>(QCoreApplication::instance())->wheelEvent(event);
|
||||
Application::getInstance()->wheelEvent(event);
|
||||
}
|
||||
|
||||
Application::Application(int& argc, char** argv) :
|
||||
|
@ -156,6 +156,7 @@ Application::Application(int& argc, char** argv) :
|
|||
_bytesCount(0)
|
||||
{
|
||||
gettimeofday(&_applicationStartupTime, NULL);
|
||||
_window->setWindowTitle("Interface");
|
||||
printLog("Interface Startup:\n");
|
||||
|
||||
_voxels.setViewFrustum(&_viewFrustum);
|
||||
|
@ -315,6 +316,10 @@ void Application::paintGL() {
|
|||
0.0f,
|
||||
//-_myAvatar.getAbsoluteHeadPitch(),
|
||||
0.0f);
|
||||
// Take a look at whether we are inside head, don't render it if so.
|
||||
const float HEAD_RENDER_DISTANCE = 0.5;
|
||||
glm::vec3 distanceToHead(_myCamera.getPosition() - _myAvatar.getSpringyHeadPosition());
|
||||
if (glm::length(distanceToHead) < HEAD_RENDER_DISTANCE) { _myAvatar.setDisplayingHead(false); }
|
||||
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setTargetPosition(_myAvatar.getHeadPosition());
|
||||
|
@ -408,14 +413,16 @@ void Application::paintGL() {
|
|||
}
|
||||
}
|
||||
|
||||
_frameCount++;
|
||||
|
||||
_frameCount++;
|
||||
|
||||
// If application has just started, report time from startup to now (first frame display)
|
||||
if (_justStarted) {
|
||||
float startupTime = (usecTimestampNow() - usecTimestamp(&_applicationStartupTime))/1000000.0;
|
||||
_justStarted = false;
|
||||
char title[30];
|
||||
snprintf(title, 30, "Interface: %4.2f seconds", startupTime);
|
||||
char title[50];
|
||||
sprintf(title, "Interface: %4.2f seconds\n", startupTime);
|
||||
printLog("%s", title);
|
||||
_window->setWindowTitle(title);
|
||||
}
|
||||
}
|
||||
|
@ -846,19 +853,18 @@ void Application::idle() {
|
|||
timeval check;
|
||||
gettimeofday(&check, NULL);
|
||||
|
||||
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time
|
||||
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran
|
||||
|
||||
if (diffclock(&_lastTimeIdle, &check) > IDLE_SIMULATE_MSECS) {
|
||||
|
||||
float deltaTime = 1.f/_fps;
|
||||
|
||||
// Use Transmitter Hand to move hand if connected, else use mouse
|
||||
if (_myAvatar.isTransmitterV2Connected()) {
|
||||
const float HAND_FORCE_SCALING = 0.05f;
|
||||
const float* handAcceleration = _myAvatar.getTransmitterHandLastAcceleration();
|
||||
_myAvatar.setMovedHandOffset(glm::vec3(-handAcceleration[0] * HAND_FORCE_SCALING,
|
||||
handAcceleration[1] * HAND_FORCE_SCALING,
|
||||
handAcceleration[2] * HAND_FORCE_SCALING));
|
||||
// Use Transmitter Hand to move hand if connected, else use mouse
|
||||
if (_myTransmitter.isConnected()) {
|
||||
const float HAND_FORCE_SCALING = 0.01f;
|
||||
glm::vec3 estimatedRotation = _myTransmitter.getEstimatedRotation();
|
||||
glm::vec3 handForce(-estimatedRotation.z, -estimatedRotation.x, estimatedRotation.y);
|
||||
_myAvatar.setMovedHandOffset(handForce * HAND_FORCE_SCALING);
|
||||
} else {
|
||||
// update behaviors for avatar hand movement: handControl takes mouse values as input,
|
||||
// and gives back 3D values modulated for smooth transitioning between interaction modes.
|
||||
|
@ -964,20 +970,26 @@ void Application::idle() {
|
|||
networkReceive(0);
|
||||
}
|
||||
|
||||
//loop through all the remote avatars and simulate them...
|
||||
//loop through all the other avatars and simulate them...
|
||||
AgentList* agentList = AgentList::getInstance();
|
||||
agentList->lock();
|
||||
for(AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
|
||||
if (agent->getLinkedData() != NULL) {
|
||||
Avatar *avatar = (Avatar *)agent->getLinkedData();
|
||||
avatar->simulate(deltaTime);
|
||||
avatar->simulate(deltaTime, false);
|
||||
avatar->setMouseRay(mouseRayOrigin, mouseRayDirection);
|
||||
}
|
||||
}
|
||||
agentList->unlock();
|
||||
|
||||
// Simulate myself
|
||||
_myAvatar.setGravity(getGravity(_myAvatar.getPosition()));
|
||||
_myAvatar.simulate(deltaTime);
|
||||
if (_transmitterDrives->isChecked() && _myTransmitter.isConnected()) {
|
||||
_myAvatar.simulate(deltaTime, &_myTransmitter);
|
||||
} else {
|
||||
_myAvatar.simulate(deltaTime, NULL);
|
||||
|
||||
}
|
||||
|
||||
// Update audio stats for procedural sounds
|
||||
#ifndef _WIN32
|
||||
|
@ -1044,7 +1056,7 @@ void Application::setRenderFirstPerson(bool firstPerson) {
|
|||
a.distance = 0.0f;
|
||||
a.tightness = 100.0f;
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON, a);
|
||||
_myAvatar.setDisplayingHead(false);
|
||||
_myAvatar.setDisplayingHead(true);
|
||||
|
||||
} else {
|
||||
Camera::CameraFollowingAttributes a;
|
||||
|
@ -1203,6 +1215,9 @@ void Application::initMenu() {
|
|||
_gyroLook->setChecked(true);
|
||||
(_mouseLook = optionsMenu->addAction("Mouse Look"))->setCheckable(true);
|
||||
_mouseLook->setChecked(false);
|
||||
(_transmitterDrives = optionsMenu->addAction("Transmitter Drive"))->setCheckable(true);
|
||||
_transmitterDrives->setChecked(true);
|
||||
|
||||
optionsMenu->addAction("Fullscreen", this, SLOT(setFullscreen(bool)), Qt::Key_F)->setCheckable(true);
|
||||
|
||||
QMenu* renderMenu = menuBar->addMenu("Render");
|
||||
|
@ -1350,7 +1365,7 @@ void Application::updateAvatar(float deltaTime) {
|
|||
float measuredYawRate = _serialPort.getLastYawRate();
|
||||
|
||||
// Update gyro-based mouse (X,Y on screen)
|
||||
const float MIN_MOUSE_RATE = 1.0;
|
||||
const float MIN_MOUSE_RATE = 3.0;
|
||||
const float HORIZONTAL_PIXELS_PER_DEGREE = 2880.f / 45.f;
|
||||
const float VERTICAL_PIXELS_PER_DEGREE = 1800.f / 30.f;
|
||||
if (powf(measuredYawRate * measuredYawRate +
|
||||
|
@ -1756,8 +1771,8 @@ void Application::displayOverlay() {
|
|||
if (_displayLevels) _serialPort.renderLevels(_glWidget->width(), _glWidget->height());
|
||||
|
||||
// Show hand transmitter data if detected
|
||||
if (_myAvatar.isTransmitterV2Connected()) {
|
||||
_myAvatar.transmitterV2RenderLevels(_glWidget->width(), _glWidget->height());
|
||||
if (_myTransmitter.isConnected()) {
|
||||
_myTransmitter.renderLevels(_glWidget->width(), _glWidget->height());
|
||||
}
|
||||
// Display stats and log text onscreen
|
||||
glLineWidth(1.0f);
|
||||
|
@ -2052,6 +2067,7 @@ void Application::resetSensors() {
|
|||
}
|
||||
QCursor::setPos(_headMouseX, _headMouseY);
|
||||
_myAvatar.reset();
|
||||
_myTransmitter.resetLevels();
|
||||
}
|
||||
|
||||
static void setShortcutsEnabled(QWidget* widget, bool enabled) {
|
||||
|
@ -2095,7 +2111,7 @@ void* Application::networkReceive(void* args) {
|
|||
sockaddr senderAddress;
|
||||
ssize_t bytesReceived;
|
||||
|
||||
Application* app = static_cast<Application*>(QCoreApplication::instance());
|
||||
Application* app = Application::getInstance();
|
||||
while (!app->_stopNetworkReceiveThread) {
|
||||
// check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that
|
||||
if (app->_wantToKillLocalVoxels) {
|
||||
|
@ -2108,13 +2124,9 @@ void* Application::networkReceive(void* args) {
|
|||
app->_bytesCount += bytesReceived;
|
||||
|
||||
switch (app->_incomingPacket[0]) {
|
||||
case PACKET_HEADER_TRANSMITTER_DATA_V1:
|
||||
// V1 = android app, or the Google Glass
|
||||
app->_myAvatar.processTransmitterData(app->_incomingPacket, bytesReceived);
|
||||
break;
|
||||
case PACKET_HEADER_TRANSMITTER_DATA_V2:
|
||||
// V2 = IOS transmitter app
|
||||
app->_myAvatar.processTransmitterDataV2(app->_incomingPacket, bytesReceived);
|
||||
app->_myTransmitter.processIncomingData(app->_incomingPacket, bytesReceived);
|
||||
|
||||
break;
|
||||
case PACKET_HEADER_MIXED_AUDIO:
|
||||
|
|
|
@ -44,6 +44,8 @@ class Application : public QApplication {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static Application* getInstance() { return static_cast<Application*>(QCoreApplication::instance()); }
|
||||
|
||||
Application(int& argc, char** argv);
|
||||
|
||||
void initializeGL();
|
||||
|
@ -60,6 +62,7 @@ public:
|
|||
void wheelEvent(QWheelEvent* event);
|
||||
|
||||
Avatar* getAvatar() { return &_myAvatar; }
|
||||
VoxelSystem* getVoxels() { return &_voxels; }
|
||||
|
||||
private slots:
|
||||
|
||||
|
@ -134,6 +137,7 @@ private:
|
|||
QAction* _lookingInMirror; // Are we currently rendering one's own head as if in mirror?
|
||||
QAction* _gyroLook; // Whether to allow the gyro data from head to move your view
|
||||
QAction* _mouseLook; // Whether the have the mouse near edge of screen move your view
|
||||
QAction* _transmitterDrives; // Whether to have Transmitter data move/steer the Avatar
|
||||
QAction* _renderVoxels; // Whether to render voxels
|
||||
QAction* _renderVoxelTextures; // Whether to render noise textures on voxels
|
||||
QAction* _renderStarsOn; // Whether to display the stars
|
||||
|
@ -189,6 +193,8 @@ private:
|
|||
|
||||
Avatar _myAvatar; // The rendered avatar of oneself
|
||||
|
||||
Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar
|
||||
|
||||
Camera _myCamera; // My view onto the world
|
||||
Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ int audioCallback (const void* inputBuffer,
|
|||
Audio* parentAudio = (Audio*) userData;
|
||||
AgentList* agentList = AgentList::getInstance();
|
||||
|
||||
Application* interface = (Application*) QCoreApplication::instance();
|
||||
Application* interface = Application::getInstance();
|
||||
Avatar* interfaceAvatar = interface->getAvatar();
|
||||
|
||||
int16_t* inputLeft = ((int16_t**) inputBuffer)[0];
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <lodepng.h>
|
||||
#include <SharedUtil.h>
|
||||
#include "world.h"
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "Head.h"
|
||||
#include "Log.h"
|
||||
|
@ -76,11 +77,6 @@ Avatar::Avatar(bool isMine) :
|
|||
_speed(0.0f),
|
||||
_maxArmLength(0.0f),
|
||||
_orientation(),
|
||||
_transmitterIsFirstData(true),
|
||||
_transmitterHz(0.0f),
|
||||
_transmitterPackets(0),
|
||||
_transmitterInitialReading(0.0f, 0.0f, 0.0f),
|
||||
_isTransmitterV2Connected(false),
|
||||
_pelvisStandingHeight(0.0f),
|
||||
_displayingHead(true),
|
||||
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
|
||||
|
@ -214,7 +210,7 @@ void Avatar::updateFromMouse(int mouseX, int mouseY, int screenWidth, int scree
|
|||
return;
|
||||
}
|
||||
|
||||
void Avatar::simulate(float deltaTime) {
|
||||
void Avatar::simulate(float deltaTime, Transmitter* transmitter) {
|
||||
|
||||
//figure out if the mouse cursor is over any body spheres...
|
||||
if (_isMine) {
|
||||
|
@ -259,11 +255,17 @@ void Avatar::simulate(float deltaTime) {
|
|||
updateCollisionWithSphere(_TEST_bigSpherePosition, _TEST_bigSphereRadius, deltaTime);
|
||||
}
|
||||
|
||||
// collision response with voxels
|
||||
if (_isMine) {
|
||||
updateCollisionWithVoxels(deltaTime);
|
||||
}
|
||||
|
||||
// driving the avatar around should only apply if this is my avatar (as opposed to an avatar being driven remotely)
|
||||
if (_isMine) {
|
||||
|
||||
_thrust = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
|
||||
// Add Thrusts from keyboard
|
||||
if (_driveKeys[FWD ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getFront();}
|
||||
if (_driveKeys[BACK ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getFront();}
|
||||
if (_driveKeys[RIGHT ]) {_thrust += THRUST_MAG * deltaTime * _orientation.getRight();}
|
||||
|
@ -272,6 +274,24 @@ void Avatar::simulate(float deltaTime) {
|
|||
if (_driveKeys[DOWN ]) {_thrust -= THRUST_MAG * deltaTime * _orientation.getUp();}
|
||||
if (_driveKeys[ROT_RIGHT]) {_bodyYawDelta -= YAW_MAG * deltaTime;}
|
||||
if (_driveKeys[ROT_LEFT ]) {_bodyYawDelta += YAW_MAG * deltaTime;}
|
||||
|
||||
// Add thrusts from Transmitter
|
||||
if (transmitter) {
|
||||
glm::vec3 rotation = transmitter->getEstimatedRotation();
|
||||
const float TRANSMITTER_MIN_RATE = 1.f;
|
||||
const float TRANSMITTER_LATERAL_FORCE_SCALE = 25.f;
|
||||
const float TRANSMITTER_FWD_FORCE_SCALE = 50.f;
|
||||
const float TRANSMITTER_YAW_SCALE = 7.0f;
|
||||
if (fabs(rotation.z) > TRANSMITTER_MIN_RATE) {
|
||||
_thrust += rotation.z * TRANSMITTER_LATERAL_FORCE_SCALE * deltaTime * _orientation.getRight();
|
||||
}
|
||||
if (fabs(rotation.x) > TRANSMITTER_MIN_RATE) {
|
||||
_thrust += -rotation.x * TRANSMITTER_FWD_FORCE_SCALE * deltaTime * _orientation.getFront();
|
||||
}
|
||||
if (fabs(rotation.y) > TRANSMITTER_MIN_RATE) {
|
||||
_bodyYawDelta += rotation.y * TRANSMITTER_YAW_SCALE * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update body yaw by body yaw delta
|
||||
|
@ -569,6 +589,20 @@ void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float d
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::updateCollisionWithVoxels(float deltaTime) {
|
||||
VoxelSystem* voxels = Application::getInstance()->getVoxels();
|
||||
float radius = _height * 0.125f;
|
||||
glm::vec3 halfVector = glm::vec3(0.0f, _height * ONE_HALF - radius, 0.0f);
|
||||
glm::vec3 penetration;
|
||||
if (voxels->findCapsulePenetration(_position - halfVector, _position + halfVector, radius, penetration)) {
|
||||
_position += penetration;
|
||||
|
||||
// reflect the velocity component in the direction of penetration
|
||||
glm::vec3 direction = glm::normalize(penetration);
|
||||
_velocity -= 2.0f * glm::dot(_velocity, direction) * direction * BOUNCE;
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::updateAvatarCollisions(float deltaTime) {
|
||||
|
||||
// Reset detector for nearest avatar
|
||||
|
@ -1133,169 +1167,6 @@ void Avatar::renderBody(bool lookingInMirror) {
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Process UDP interface data from Android transmitter or Google Glass
|
||||
//
|
||||
void Avatar::processTransmitterData(unsigned char* packetData, int numBytes) {
|
||||
// Read a packet from a transmitter app, process the data
|
||||
float
|
||||
accX, accY, accZ, // Measured acceleration
|
||||
graX, graY, graZ, // Gravity
|
||||
gyrX, gyrY, gyrZ, // Gyro velocity in radians/sec as (pitch, roll, yaw)
|
||||
linX, linY, linZ, // Linear Acceleration (less gravity)
|
||||
rot1, rot2, rot3, rot4; // Rotation of device:
|
||||
// rot1 = roll, ranges from -1 to 1, 0 = flat on table
|
||||
// rot2 = pitch, ranges from -1 to 1, 0 = flat on table
|
||||
// rot3 = yaw, ranges from -1 to 1
|
||||
char device[100]; // Device ID
|
||||
|
||||
enum deviceTypes { DEVICE_GLASS, DEVICE_ANDROID, DEVICE_IPHONE, DEVICE_UNKNOWN };
|
||||
|
||||
sscanf((char *)packetData,
|
||||
"tacc %f %f %f gra %f %f %f gyr %f %f %f lin %f %f %f rot %f %f %f %f dna \"%s",
|
||||
&accX, &accY, &accZ,
|
||||
&graX, &graY, &graZ,
|
||||
&gyrX, &gyrY, &gyrZ,
|
||||
&linX, &linY, &linZ,
|
||||
&rot1, &rot2, &rot3, &rot4, (char *)&device);
|
||||
|
||||
// decode transmitter device type
|
||||
deviceTypes deviceType = DEVICE_UNKNOWN;
|
||||
if (strcmp(device, "ADR")) {
|
||||
deviceType = DEVICE_ANDROID;
|
||||
} else {
|
||||
deviceType = DEVICE_GLASS;
|
||||
}
|
||||
|
||||
if (_transmitterPackets++ == 0) {
|
||||
// If first packet received, note time, turn head spring return OFF, get start rotation
|
||||
gettimeofday(&_transmitterTimer, NULL);
|
||||
if (deviceType == DEVICE_GLASS) {
|
||||
_head.setReturnToCenter(true);
|
||||
_head.setSpringScale(10.f);
|
||||
printLog("Using Google Glass to drive head, springs ON.\n");
|
||||
|
||||
} else {
|
||||
_head.setReturnToCenter(false);
|
||||
printLog("Using Transmitter %s to drive head, springs OFF.\n", device);
|
||||
|
||||
}
|
||||
//printLog("Packet: [%s]\n", packetData);
|
||||
//printLog("Version: %s\n", device);
|
||||
|
||||
_transmitterInitialReading = glm::vec3(rot3, rot2, rot1);
|
||||
}
|
||||
|
||||
const int TRANSMITTER_COUNT = 100;
|
||||
if (_transmitterPackets % TRANSMITTER_COUNT == 0) {
|
||||
// Every 100 packets, record the observed Hz of the transmitter data
|
||||
timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
double msecsElapsed = diffclock(&_transmitterTimer, &now);
|
||||
_transmitterHz = static_cast<float>((double)TRANSMITTER_COUNT / (msecsElapsed / 1000.0));
|
||||
_transmitterTimer = now;
|
||||
printLog("Transmitter Hz: %3.1f\n", _transmitterHz);
|
||||
}
|
||||
//printLog("Gyr: %3.1f, %3.1f, %3.1f\n", glm::degrees(gyrZ), glm::degrees(-gyrX), glm::degrees(gyrY));
|
||||
//printLog("Rot: %3.1f, %3.1f, %3.1f, %3.1f\n", rot1, rot2, rot3, rot4);
|
||||
|
||||
// Update the head with the transmitter data
|
||||
glm::vec3 eulerAngles((rot3 - _transmitterInitialReading.x) * 180.f,
|
||||
-(rot2 - _transmitterInitialReading.y) * 180.f,
|
||||
(rot1 - _transmitterInitialReading.z) * 180.f);
|
||||
if (eulerAngles.x > 180.f) { eulerAngles.x -= 360.f; }
|
||||
if (eulerAngles.x < -180.f) { eulerAngles.x += 360.f; }
|
||||
|
||||
glm::vec3 angularVelocity;
|
||||
if (deviceType != DEVICE_GLASS) {
|
||||
angularVelocity = glm::vec3(glm::degrees(gyrZ), glm::degrees(-gyrX), glm::degrees(gyrY));
|
||||
setHeadFromGyros(&eulerAngles, &angularVelocity,
|
||||
(_transmitterHz == 0.f) ? 0.f : 1.f / _transmitterHz, 1.0);
|
||||
|
||||
} else {
|
||||
angularVelocity = glm::vec3(glm::degrees(gyrY), glm::degrees(-gyrX), glm::degrees(-gyrZ));
|
||||
setHeadFromGyros(&eulerAngles, &angularVelocity,
|
||||
(_transmitterHz == 0.f) ? 0.f : 1.f / _transmitterHz, 1000.0);
|
||||
}
|
||||
}
|
||||
//
|
||||
// Process UDP data from version 2 Transmitter acting as Hand
|
||||
//
|
||||
void Avatar::processTransmitterDataV2(unsigned char* packetData, int numBytes) {
|
||||
if (numBytes == 3 + sizeof(_transmitterHandLastRotationRates) +
|
||||
sizeof(_transmitterHandLastAcceleration)) {
|
||||
memcpy(_transmitterHandLastRotationRates, packetData + 2,
|
||||
sizeof(_transmitterHandLastRotationRates));
|
||||
memcpy(_transmitterHandLastAcceleration, packetData + 3 +
|
||||
sizeof(_transmitterHandLastRotationRates),
|
||||
sizeof(_transmitterHandLastAcceleration));
|
||||
// Convert from transmitter units to internal units
|
||||
for (int i = 0; i < 3; i++) {
|
||||
_transmitterHandLastRotationRates[i] *= 180.f / PI;
|
||||
_transmitterHandLastAcceleration[i] *= GRAVITY_EARTH;
|
||||
}
|
||||
if (!_isTransmitterV2Connected) {
|
||||
printf("Transmitter V2 Connected.\n");
|
||||
_isTransmitterV2Connected = true;
|
||||
}
|
||||
} else {
|
||||
printf("Transmitter V2 packet read error.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::transmitterV2RenderLevels(int width, int height) {
|
||||
|
||||
char val[50];
|
||||
const int LEVEL_CORNER_X = 10;
|
||||
const int LEVEL_CORNER_Y = 400;
|
||||
|
||||
// Draw the numeric degree/sec values from the gyros
|
||||
sprintf(val, "Yaw %4.1f", _transmitterHandLastRotationRates[1]);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y, 0.10, 0, 1.0, 1, val, 0, 1, 0);
|
||||
sprintf(val, "Pitch %4.1f", _transmitterHandLastRotationRates[0]);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 15, 0.10, 0, 1.0, 1, val, 0, 1, 0);
|
||||
sprintf(val, "Roll %4.1f", _transmitterHandLastRotationRates[2]);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 30, 0.10, 0, 1.0, 1, val, 0, 1, 0);
|
||||
sprintf(val, "X %4.3f", _transmitterHandLastAcceleration[0]);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 45, 0.10, 0, 1.0, 1, val, 0, 1, 0);
|
||||
sprintf(val, "Y %4.3f", _transmitterHandLastAcceleration[1]);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 60, 0.10, 0, 1.0, 1, val, 0, 1, 0);
|
||||
sprintf(val, "Z %4.3f", _transmitterHandLastAcceleration[2]);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 75, 0.10, 0, 1.0, 1, val, 0, 1, 0);
|
||||
|
||||
// Draw the levels as horizontal lines
|
||||
const int LEVEL_CENTER = 150;
|
||||
const float ACCEL_VIEW_SCALING = 50.f;
|
||||
glLineWidth(2.0);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
glBegin(GL_LINES);
|
||||
// Gyro rates
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y - 3);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + _transmitterHandLastRotationRates[1], LEVEL_CORNER_Y - 3);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 12);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + _transmitterHandLastRotationRates[0], LEVEL_CORNER_Y + 12);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 27);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + _transmitterHandLastRotationRates[2], LEVEL_CORNER_Y + 27);
|
||||
// Acceleration
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 42);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)(_transmitterHandLastAcceleration[0] * ACCEL_VIEW_SCALING),
|
||||
LEVEL_CORNER_Y + 42);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 57);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)(_transmitterHandLastAcceleration[1] * ACCEL_VIEW_SCALING),
|
||||
LEVEL_CORNER_Y + 57);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 72);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)(_transmitterHandLastAcceleration[2] * ACCEL_VIEW_SCALING),
|
||||
LEVEL_CORNER_Y + 72);
|
||||
|
||||
glEnd();
|
||||
// Draw green vertical centerline
|
||||
glColor4f(0, 1, 0, 0.5);
|
||||
glBegin(GL_LINES);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y - 6);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 30);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void Avatar::setHeadFromGyros(glm::vec3* eulerAngles, glm::vec3* angularVelocity, float deltaTime, float smoothingTime) {
|
||||
//
|
||||
// Given absolute position and angular velocity information, update the avatar's head angles
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "SerialInterface.h"
|
||||
#include "Balls.h"
|
||||
#include "Head.h"
|
||||
#include "Transmitter.h"
|
||||
|
||||
enum DriveKeys
|
||||
{
|
||||
|
@ -109,7 +110,7 @@ public:
|
|||
void setMousePressed(bool pressed);
|
||||
void render(bool lookingInMirror, glm::vec3 cameraPosition);
|
||||
void renderBody(bool lookingInMirror);
|
||||
void simulate(float);
|
||||
void simulate(float deltaTime, Transmitter* transmitter);
|
||||
void setMovedHandOffset(glm::vec3 movedHandOffset) { _movedHandOffset = movedHandOffset; }
|
||||
void updateArmIKAndConstraints( float deltaTime );
|
||||
void setDisplayingHead( bool displayingHead );
|
||||
|
@ -122,16 +123,6 @@ public:
|
|||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; };
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
// Related to getting transmitter UDP data used to animate the avatar hand
|
||||
void processTransmitterData(unsigned char * packetData, int numBytes);
|
||||
void processTransmitterDataV2(unsigned char * packetData, int numBytes);
|
||||
const bool isTransmitterV2Connected() const { return _isTransmitterV2Connected; };
|
||||
const float* getTransmitterHandLastAcceleration() const { return _transmitterHandLastAcceleration; };
|
||||
const float* getTransmitterHandLastRotationRates() const { return _transmitterHandLastRotationRates; };
|
||||
void transmitterV2RenderLevels(int width, int height);
|
||||
|
||||
float getTransmitterHz() { return _transmitterHz; };
|
||||
|
||||
void writeAvatarDataToFile();
|
||||
void readAvatarDataFromFile();
|
||||
|
@ -179,15 +170,6 @@ private:
|
|||
float _maxArmLength;
|
||||
Orientation _orientation;
|
||||
int _driveKeys[MAX_DRIVE_KEYS];
|
||||
bool _transmitterIsFirstData;
|
||||
timeval _transmitterTimeLastReceived;
|
||||
timeval _transmitterTimer;
|
||||
float _transmitterHz;
|
||||
int _transmitterPackets;
|
||||
glm::vec3 _transmitterInitialReading;
|
||||
float _transmitterHandLastRotationRates[3];
|
||||
float _transmitterHandLastAcceleration[3];
|
||||
bool _isTransmitterV2Connected;
|
||||
float _pelvisStandingHeight;
|
||||
float _height;
|
||||
Balls* _balls;
|
||||
|
@ -212,6 +194,7 @@ private:
|
|||
void updateHandMovementAndTouching(float deltaTime);
|
||||
void updateAvatarCollisions(float deltaTime);
|
||||
void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );
|
||||
void updateCollisionWithVoxels(float deltaTime);
|
||||
void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
|
||||
void setHeadFromGyros(glm::vec3 * eulerAngles, glm::vec3 * angularVelocity, float deltaTime, float smoothingTime);
|
||||
void checkForMouseRayTouching();
|
||||
|
|
|
@ -659,7 +659,8 @@ void VoxelSystem::render(bool texture) {
|
|||
// draw the number of voxels we have
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID);
|
||||
glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
|
||||
glDrawElements(GL_TRIANGLES, 36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
|
||||
glDrawRangeElementsEXT(GL_TRIANGLES, 0, VERTICES_PER_VOXEL * _voxelsInReadArrays,
|
||||
36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
@ -925,6 +926,20 @@ bool VoxelSystem::findRayIntersection(const glm::vec3& origin, const glm::vec3&
|
|||
return true;
|
||||
}
|
||||
|
||||
bool VoxelSystem::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) {
|
||||
pthread_mutex_lock(&_treeLock);
|
||||
bool result = _tree->findSpherePenetration(center, radius, penetration);
|
||||
pthread_mutex_unlock(&_treeLock);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VoxelSystem::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) {
|
||||
pthread_mutex_lock(&_treeLock);
|
||||
bool result = _tree->findCapsulePenetration(start, end, radius, penetration);
|
||||
pthread_mutex_unlock(&_treeLock);
|
||||
return result;
|
||||
}
|
||||
|
||||
class falseColorizeRandomEveryOtherArgs {
|
||||
public:
|
||||
falseColorizeRandomEveryOtherArgs() : totalNodes(0), colorableNodes(0), coloredNodes(0), colorThis(true) {};
|
||||
|
|
|
@ -69,7 +69,10 @@ public:
|
|||
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
VoxelDetail& detail, float& distance, BoxFace& face);
|
||||
|
||||
|
||||
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration);
|
||||
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration);
|
||||
|
||||
void collectStatsForTreesAndVBOs();
|
||||
|
||||
void deleteVoxelAt(float x, float y, float z, float s);
|
||||
|
|
|
@ -28,7 +28,6 @@ const PACKET_HEADER PACKET_HEADER_ERASE_VOXEL = 'E';
|
|||
const PACKET_HEADER PACKET_HEADER_VOXEL_DATA = 'V';
|
||||
const PACKET_HEADER PACKET_HEADER_VOXEL_DATA_MONOCHROME = 'v';
|
||||
const PACKET_HEADER PACKET_HEADER_BULK_AVATAR_DATA = 'X';
|
||||
const PACKET_HEADER PACKET_HEADER_TRANSMITTER_DATA_V1 = 't';
|
||||
const PACKET_HEADER PACKET_HEADER_TRANSMITTER_DATA_V2 = 'T';
|
||||
const PACKET_HEADER PACKET_HEADER_ENVIRONMENT_DATA = 'e';
|
||||
const PACKET_HEADER PACKET_HEADER_DOMAIN_LIST_REQUEST = 'L';
|
||||
|
|
|
@ -286,6 +286,10 @@ void VoxelNode::printDebugDetails(const char* label) const {
|
|||
printOctalCode(_octalCode);
|
||||
}
|
||||
|
||||
float VoxelNode::getEnclosingRadius() const {
|
||||
return getScale() * sqrtf(3.0f) / 2.0f;
|
||||
}
|
||||
|
||||
bool VoxelNode::isInView(const ViewFrustum& viewFrustum) const {
|
||||
AABox box = _box; // use temporary box so we can scale it
|
||||
box.scale(TREE_SCALE);
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
float getScale() const { return _box.getSize().x; /* voxelScale = (1 / powf(2, *node->getOctalCode())); */ };
|
||||
int getLevel() const { return *_octalCode + 1; /* one based or zero based? */ };
|
||||
|
||||
float getEnclosingRadius() const;
|
||||
|
||||
bool isColored() const { return (_trueColor[3]==1); };
|
||||
bool isInView(const ViewFrustum& viewFrustum) const;
|
||||
ViewFrustum::location inFrustum(const ViewFrustum& viewFrustum) const;
|
||||
|
|
|
@ -675,7 +675,7 @@ public:
|
|||
bool found;
|
||||
};
|
||||
|
||||
bool findRayOperation(VoxelNode* node, void* extraData) {
|
||||
bool findRayIntersectionOp(VoxelNode* node, void* extraData) {
|
||||
RayArgs* args = static_cast<RayArgs*>(extraData);
|
||||
AABox box = node->getAABox();
|
||||
float distance;
|
||||
|
@ -697,10 +697,97 @@ bool findRayOperation(VoxelNode* node, void* extraData) {
|
|||
}
|
||||
|
||||
bool VoxelTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
VoxelNode*& node, float& distance, BoxFace& face)
|
||||
{
|
||||
VoxelNode*& node, float& distance, BoxFace& face) {
|
||||
RayArgs args = { origin / (float)TREE_SCALE, direction, node, distance, face };
|
||||
recurseTreeWithOperation(findRayOperation, &args);
|
||||
recurseTreeWithOperation(findRayIntersectionOp, &args);
|
||||
return args.found;
|
||||
}
|
||||
|
||||
class SphereArgs {
|
||||
public:
|
||||
glm::vec3 center;
|
||||
float radius;
|
||||
glm::vec3& penetration;
|
||||
bool found;
|
||||
};
|
||||
|
||||
bool findSpherePenetrationOp(VoxelNode* node, void* extraData) {
|
||||
SphereArgs* args = static_cast<SphereArgs*>(extraData);
|
||||
|
||||
// currently, we treat each node as a sphere enveloping the box
|
||||
const glm::vec3& nodeCenter = node->getCenter();
|
||||
glm::vec3 vector = args->center - nodeCenter;
|
||||
float vectorLength = glm::length(vector);
|
||||
float distance = vectorLength - node->getEnclosingRadius() - args->radius;
|
||||
if (distance >= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
if (!node->isLeaf()) {
|
||||
return true; // recurse on children
|
||||
}
|
||||
if (node->isColored()) {
|
||||
args->penetration += vector * (-distance * TREE_SCALE / vectorLength);
|
||||
args->found = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VoxelTree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) {
|
||||
SphereArgs args = { center / (float)TREE_SCALE, radius / TREE_SCALE, penetration };
|
||||
penetration = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
recurseTreeWithOperation(findSpherePenetrationOp, &args);
|
||||
return args.found;
|
||||
}
|
||||
|
||||
class CapsuleArgs {
|
||||
public:
|
||||
glm::vec3 start;
|
||||
glm::vec3 end;
|
||||
float radius;
|
||||
glm::vec3& penetration;
|
||||
bool found;
|
||||
};
|
||||
|
||||
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
|
||||
// compute the projection of the point vector onto the segment vector
|
||||
glm::vec3 segmentVector = end - start;
|
||||
float proj = glm::dot(point - start, segmentVector) / glm::dot(segmentVector, segmentVector);
|
||||
if (proj <= 0.0f) { // closest to the start
|
||||
return start - point;
|
||||
|
||||
} else if (proj >= 1.0f) { // closest to the end
|
||||
return end - point;
|
||||
|
||||
} else { // closest to the middle
|
||||
return start + segmentVector*proj - point;
|
||||
}
|
||||
}
|
||||
|
||||
bool findCapsulePenetrationOp(VoxelNode* node, void* extraData) {
|
||||
CapsuleArgs* args = static_cast<CapsuleArgs*>(extraData);
|
||||
|
||||
// currently, we treat each node as a sphere enveloping the box
|
||||
const glm::vec3& nodeCenter = node->getCenter();
|
||||
glm::vec3 vector = computeVectorFromPointToSegment(nodeCenter, args->start, args->end);
|
||||
float vectorLength = glm::length(vector);
|
||||
float distance = vectorLength - node->getEnclosingRadius() - args->radius;
|
||||
if (distance >= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
if (!node->isLeaf()) {
|
||||
return true; // recurse on children
|
||||
}
|
||||
if (node->isColored()) {
|
||||
args->penetration += vector * (-distance * TREE_SCALE / vectorLength);
|
||||
args->found = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VoxelTree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) {
|
||||
CapsuleArgs args = { start / (float)TREE_SCALE, end / (float)TREE_SCALE, radius / TREE_SCALE, penetration };
|
||||
penetration = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
recurseTreeWithOperation(findCapsulePenetrationOp, &args);
|
||||
return args.found;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,9 @@ public:
|
|||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
VoxelNode*& node, float& distance, BoxFace& face);
|
||||
|
||||
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration);
|
||||
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration);
|
||||
|
||||
// Note: this assumes the fileFormat is the HIO individual voxels code files
|
||||
void loadVoxelsFile(const char* fileName, bool wantColorRandomizer);
|
||||
|
||||
|
|
Loading…
Reference in a new issue