mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 19:02:55 +02:00
Merge branch 'master' of github.com:worklist/hifi into assignment
This commit is contained in:
commit
e774419f8d
30 changed files with 887 additions and 378 deletions
|
@ -21,4 +21,3 @@ add_subdirectory(injector)
|
||||||
add_subdirectory(pairing-server)
|
add_subdirectory(pairing-server)
|
||||||
add_subdirectory(space-server)
|
add_subdirectory(space-server)
|
||||||
add_subdirectory(voxel-edit)
|
add_subdirectory(voxel-edit)
|
||||||
add_subdirectory(voxel-server)
|
|
|
@ -137,7 +137,7 @@ void AvatarMixer::run() {
|
||||||
case PACKET_TYPE_INJECT_AUDIO:
|
case PACKET_TYPE_INJECT_AUDIO:
|
||||||
broadcastAvatarData(nodeList, nodeAddress);
|
broadcastAvatarData(nodeList, nodeAddress);
|
||||||
break;
|
break;
|
||||||
case PACKET_TYPE_AVATAR_VOXEL_URL:
|
case PACKET_TYPE_AVATAR_URLS:
|
||||||
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
||||||
// grab the node ID from the packet
|
// grab the node ID from the packet
|
||||||
unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID);
|
unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID);
|
||||||
|
|
|
@ -321,7 +321,7 @@ void Application::initializeGL() {
|
||||||
Menu::getInstance()->checkForUpdates();
|
Menu::getInstance()->checkForUpdates();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
InfoView::showFirstTime(Menu::getInstance());
|
InfoView::showFirstTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::paintGL() {
|
void Application::paintGL() {
|
||||||
|
@ -1230,15 +1230,19 @@ static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& da
|
||||||
return avatar->isInitialized() ? avatar : NULL;
|
return avatar->isInitialized() ? avatar : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::processAvatarVoxelURLMessage(unsigned char* packetData, size_t dataBytes) {
|
void Application::processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes) {
|
||||||
Avatar* avatar = processAvatarMessageHeader(packetData, dataBytes);
|
Avatar* avatar = processAvatarMessageHeader(packetData, dataBytes);
|
||||||
if (!avatar) {
|
if (!avatar) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QUrl url = QUrl::fromEncoded(QByteArray((char*)packetData, dataBytes));
|
QDataStream in(QByteArray((char*)packetData, dataBytes));
|
||||||
|
QUrl voxelURL, faceURL;
|
||||||
|
in >> voxelURL;
|
||||||
|
in >> faceURL;
|
||||||
|
|
||||||
// invoke the set URL function on the simulate/render thread
|
// invoke the set URL functions on the simulate/render thread
|
||||||
QMetaObject::invokeMethod(avatar->getVoxels(), "setVoxelURL", Q_ARG(QUrl, url));
|
QMetaObject::invokeMethod(avatar->getVoxels(), "setVoxelURL", Q_ARG(QUrl, voxelURL));
|
||||||
|
QMetaObject::invokeMethod(&avatar->getHead().getBlendFace(), "setModelURL", Q_ARG(QUrl, faceURL));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
void Application::processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes) {
|
||||||
|
@ -1564,7 +1568,7 @@ void Application::init() {
|
||||||
|
|
||||||
qDebug("Loaded settings.\n");
|
qDebug("Loaded settings.\n");
|
||||||
|
|
||||||
Avatar::sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL());
|
Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL(), _myAvatar.getHead().getBlendFace().getModelURL());
|
||||||
|
|
||||||
_palette.init(_glWidget->width(), _glWidget->height());
|
_palette.init(_glWidget->width(), _glWidget->height());
|
||||||
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0);
|
_palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0);
|
||||||
|
@ -2136,10 +2140,11 @@ void Application::updateAvatar(float deltaTime) {
|
||||||
controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString,
|
controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString,
|
||||||
nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
|
nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
|
||||||
|
|
||||||
// once in a while, send my voxel url
|
// once in a while, send my urls
|
||||||
const float AVATAR_VOXEL_URL_SEND_INTERVAL = 1.0f; // seconds
|
const float AVATAR_URLS_SEND_INTERVAL = 1.0f; // seconds
|
||||||
if (shouldDo(AVATAR_VOXEL_URL_SEND_INTERVAL, deltaTime)) {
|
if (shouldDo(AVATAR_URLS_SEND_INTERVAL, deltaTime)) {
|
||||||
Avatar::sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL());
|
Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL(),
|
||||||
|
_myAvatar.getHead().getBlendFace().getModelURL());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3510,8 +3515,8 @@ void* Application::networkReceive(void* args) {
|
||||||
bytesReceived);
|
bytesReceived);
|
||||||
getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
|
getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived);
|
||||||
break;
|
break;
|
||||||
case PACKET_TYPE_AVATAR_VOXEL_URL:
|
case PACKET_TYPE_AVATAR_URLS:
|
||||||
processAvatarVoxelURLMessage(app->_incomingPacket, bytesReceived);
|
processAvatarURLsMessage(app->_incomingPacket, bytesReceived);
|
||||||
break;
|
break;
|
||||||
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
||||||
processAvatarFaceVideoMessage(app->_incomingPacket, bytesReceived);
|
processAvatarFaceVideoMessage(app->_incomingPacket, bytesReceived);
|
||||||
|
|
|
@ -187,7 +187,7 @@ private:
|
||||||
void updateProjectionMatrix();
|
void updateProjectionMatrix();
|
||||||
|
|
||||||
static bool sendVoxelsOperation(VoxelNode* node, void* extraData);
|
static bool sendVoxelsOperation(VoxelNode* node, void* extraData);
|
||||||
static void processAvatarVoxelURLMessage(unsigned char* packetData, size_t dataBytes);
|
static void processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes);
|
||||||
static void processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes);
|
static void processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes);
|
||||||
static void sendPingPackets();
|
static void sendPingPackets();
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
#define SETTINGS_VERSION_KEY "info-version"
|
#define SETTINGS_VERSION_KEY "info-version"
|
||||||
#define MAX_DIALOG_HEIGHT_RATIO 0.9
|
#define MAX_DIALOG_HEIGHT_RATIO 0.9
|
||||||
|
|
||||||
InfoView::InfoView(bool forced, QWidget* parent) :
|
InfoView::InfoView(bool forced) :
|
||||||
QWebView(parent),
|
|
||||||
_forced(forced) {
|
_forced(forced) {
|
||||||
|
|
||||||
switchToResourcesParentIfRequired();
|
switchToResourcesParentIfRequired();
|
||||||
|
@ -29,12 +28,12 @@ InfoView::InfoView(bool forced, QWidget* parent) :
|
||||||
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loaded(bool)));
|
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loaded(bool)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfoView::showFirstTime(QWidget* parent) {
|
void InfoView::showFirstTime() {
|
||||||
new InfoView(false, parent);
|
new InfoView(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfoView::forcedShow(QWidget* parent) {
|
void InfoView::forcedShow() {
|
||||||
new InfoView(true, parent);
|
new InfoView(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InfoView::shouldShow() {
|
bool InfoView::shouldShow() {
|
||||||
|
@ -59,6 +58,7 @@ bool InfoView::shouldShow() {
|
||||||
|
|
||||||
void InfoView::loaded(bool ok) {
|
void InfoView::loaded(bool ok) {
|
||||||
if (!ok || !shouldShow()) {
|
if (!ok || !shouldShow()) {
|
||||||
|
deleteLater();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,5 +72,6 @@ void InfoView::loaded(bool ok) {
|
||||||
resize(mainFrame->contentsSize().width(), height);
|
resize(mainFrame->contentsSize().width(), height);
|
||||||
move(desktop->screen()->rect().center() - rect().center());
|
move(desktop->screen()->rect().center() - rect().center());
|
||||||
setWindowTitle(title());
|
setWindowTitle(title());
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,11 @@
|
||||||
class InfoView : public QWebView {
|
class InfoView : public QWebView {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
static void showFirstTime(QWidget* parent);
|
static void showFirstTime();
|
||||||
static void forcedShow(QWidget* parent);
|
static void forcedShow();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
InfoView(bool forced, QWidget* parent);
|
InfoView(bool forced);
|
||||||
bool _forced;
|
bool _forced;
|
||||||
bool shouldShow();
|
bool shouldShow();
|
||||||
|
|
||||||
|
|
|
@ -275,7 +275,8 @@ Menu::Menu() :
|
||||||
appInstance->getGlowEffect(),
|
appInstance->getGlowEffect(),
|
||||||
SLOT(cycleRenderMode()));
|
SLOT(cycleRenderMode()));
|
||||||
|
|
||||||
|
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::UseFaceshiftRig, 0, false,
|
||||||
|
appInstance->getFaceshift(), SLOT(setUsingRig(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::UsePerlinFace, 0, false);
|
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::UsePerlinFace, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtVectors, 0, true);
|
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtVectors, 0, true);
|
||||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtIndicator, 0, true);
|
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtIndicator, 0, true);
|
||||||
|
@ -684,7 +685,7 @@ bool Menu::isVoxelModeActionChecked() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::aboutApp() {
|
void Menu::aboutApp() {
|
||||||
InfoView::forcedShow(this);
|
InfoView::forcedShow();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateDSHostname(const QString& domainServerHostname) {
|
void updateDSHostname(const QString& domainServerHostname) {
|
||||||
|
@ -746,6 +747,10 @@ void Menu::editPreferences() {
|
||||||
avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||||
form->addRow("Avatar URL:", avatarURL);
|
form->addRow("Avatar URL:", avatarURL);
|
||||||
|
|
||||||
|
QLineEdit* faceURL = new QLineEdit(applicationInstance->getAvatar()->getHead().getBlendFace().getModelURL().toString());
|
||||||
|
faceURL->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||||
|
form->addRow("Face URL:", faceURL);
|
||||||
|
|
||||||
QSpinBox* fieldOfView = new QSpinBox();
|
QSpinBox* fieldOfView = new QSpinBox();
|
||||||
fieldOfView->setMaximum(180);
|
fieldOfView->setMaximum(180);
|
||||||
fieldOfView->setMinimum(1);
|
fieldOfView->setMinimum(1);
|
||||||
|
@ -779,9 +784,13 @@ void Menu::editPreferences() {
|
||||||
|
|
||||||
updateDSHostname(domainServerLineEdit->text());
|
updateDSHostname(domainServerLineEdit->text());
|
||||||
|
|
||||||
QUrl url(avatarURL->text());
|
QUrl avatarVoxelURL(avatarURL->text());
|
||||||
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(url);
|
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
|
||||||
Avatar::sendAvatarVoxelURLMessage(url);
|
|
||||||
|
QUrl faceModelURL(faceURL->text());
|
||||||
|
applicationInstance->getAvatar()->getHead().getBlendFace().setModelURL(faceModelURL);
|
||||||
|
|
||||||
|
Avatar::sendAvatarURLsMessage(avatarVoxelURL, faceModelURL);
|
||||||
|
|
||||||
_gyroCameraSensitivity = gyroCameraSensitivity->value();
|
_gyroCameraSensitivity = gyroCameraSensitivity->value();
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ struct ViewFrustumOffset {
|
||||||
float up;
|
float up;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class QSettings;
|
||||||
|
|
||||||
class BandwidthDialog;
|
class BandwidthDialog;
|
||||||
class VoxelStatsDialog;
|
class VoxelStatsDialog;
|
||||||
|
|
||||||
|
@ -197,6 +199,7 @@ namespace MenuOption {
|
||||||
const QString TestRaveGlove = "Test Rave Glove";
|
const QString TestRaveGlove = "Test Rave Glove";
|
||||||
const QString TreeStats = "Calculate Tree Stats";
|
const QString TreeStats = "Calculate Tree Stats";
|
||||||
const QString TransmitterDrive = "Transmitter Drive";
|
const QString TransmitterDrive = "Transmitter Drive";
|
||||||
|
const QString UseFaceshiftRig = "Use Faceshift Rig";
|
||||||
const QString UsePerlinFace = "Use Perlin's Face";
|
const QString UsePerlinFace = "Use Perlin's Face";
|
||||||
const QString Quit = "Quit";
|
const QString Quit = "Quit";
|
||||||
const QString Webcam = "Webcam";
|
const QString Webcam = "Webcam";
|
||||||
|
|
|
@ -57,11 +57,10 @@ const float HEAD_RATE_MAX = 50.f;
|
||||||
const float SKIN_COLOR[] = {1.0, 0.84, 0.66};
|
const float SKIN_COLOR[] = {1.0, 0.84, 0.66};
|
||||||
const float DARK_SKIN_COLOR[] = {0.9, 0.78, 0.63};
|
const float DARK_SKIN_COLOR[] = {0.9, 0.78, 0.63};
|
||||||
const int NUM_BODY_CONE_SIDES = 9;
|
const int NUM_BODY_CONE_SIDES = 9;
|
||||||
const bool usingBigSphereCollisionTest = true;
|
|
||||||
const float chatMessageScale = 0.0015;
|
const float chatMessageScale = 0.0015;
|
||||||
const float chatMessageHeight = 0.20;
|
const float chatMessageHeight = 0.20;
|
||||||
|
|
||||||
void Avatar::sendAvatarVoxelURLMessage(const QUrl& url) {
|
void Avatar::sendAvatarURLsMessage(const QUrl& voxelURL, const QUrl& faceURL) {
|
||||||
uint16_t ownerID = NodeList::getInstance()->getOwnerID();
|
uint16_t ownerID = NodeList::getInstance()->getOwnerID();
|
||||||
|
|
||||||
if (ownerID == UNKNOWN_NODE_ID) {
|
if (ownerID == UNKNOWN_NODE_ID) {
|
||||||
|
@ -71,11 +70,14 @@ void Avatar::sendAvatarVoxelURLMessage(const QUrl& url) {
|
||||||
QByteArray message;
|
QByteArray message;
|
||||||
|
|
||||||
char packetHeader[MAX_PACKET_HEADER_BYTES];
|
char packetHeader[MAX_PACKET_HEADER_BYTES];
|
||||||
int numBytesPacketHeader = populateTypeAndVersion((unsigned char*) packetHeader, PACKET_TYPE_AVATAR_VOXEL_URL);
|
int numBytesPacketHeader = populateTypeAndVersion((unsigned char*) packetHeader, PACKET_TYPE_AVATAR_URLS);
|
||||||
|
|
||||||
message.append(packetHeader, numBytesPacketHeader);
|
message.append(packetHeader, numBytesPacketHeader);
|
||||||
message.append((const char*)&ownerID, sizeof(ownerID));
|
message.append((const char*)&ownerID, sizeof(ownerID));
|
||||||
message.append(url.toEncoded());
|
|
||||||
|
QDataStream out(&message, QIODevice::WriteOnly | QIODevice::Append);
|
||||||
|
out << voxelURL;
|
||||||
|
out << faceURL;
|
||||||
|
|
||||||
Application::controlledBroadcastToNodes((unsigned char*)message.data(), message.size(), &NODE_TYPE_AVATAR_MIXER, 1);
|
Application::controlledBroadcastToNodes((unsigned char*)message.data(), message.size(), &NODE_TYPE_AVATAR_MIXER, 1);
|
||||||
}
|
}
|
||||||
|
@ -85,8 +87,6 @@ Avatar::Avatar(Node* owningNode) :
|
||||||
_head(this),
|
_head(this),
|
||||||
_hand(this),
|
_hand(this),
|
||||||
_ballSpringsInitialized(false),
|
_ballSpringsInitialized(false),
|
||||||
_TEST_bigSphereRadius(0.5f),
|
|
||||||
_TEST_bigSpherePosition(5.0f, _TEST_bigSphereRadius, 5.0f),
|
|
||||||
_bodyYawDelta(0.0f),
|
_bodyYawDelta(0.0f),
|
||||||
_movedHandOffset(0.0f, 0.0f, 0.0f),
|
_movedHandOffset(0.0f, 0.0f, 0.0f),
|
||||||
_mode(AVATAR_MODE_STANDING),
|
_mode(AVATAR_MODE_STANDING),
|
||||||
|
@ -362,12 +362,6 @@ void Avatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCamer
|
||||||
// update body balls
|
// update body balls
|
||||||
updateBodyBalls(deltaTime);
|
updateBodyBalls(deltaTime);
|
||||||
|
|
||||||
|
|
||||||
// test for avatar collision response with the big sphere
|
|
||||||
if (usingBigSphereCollisionTest && _isCollisionsOn) {
|
|
||||||
updateCollisionWithSphere(_TEST_bigSpherePosition, _TEST_bigSphereRadius, deltaTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
//apply the head lean values to the ball positions...
|
//apply the head lean values to the ball positions...
|
||||||
if (USING_HEAD_LEAN) {
|
if (USING_HEAD_LEAN) {
|
||||||
if (fabs(_head.getLeanSideways() + _head.getLeanForward()) > 0.0f) {
|
if (fabs(_head.getLeanSideways() + _head.getLeanForward()) > 0.0f) {
|
||||||
|
@ -446,32 +440,6 @@ void Avatar::updateHandMovementAndTouching(float deltaTime, bool enableHandMovem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::updateCollisionWithSphere(glm::vec3 position, float radius, float deltaTime) {
|
|
||||||
float myBodyApproximateBoundingRadius = 1.0f;
|
|
||||||
glm::vec3 vectorFromMyBodyToBigSphere(_position - position);
|
|
||||||
|
|
||||||
float distanceToBigSphere = glm::length(vectorFromMyBodyToBigSphere);
|
|
||||||
if (distanceToBigSphere < myBodyApproximateBoundingRadius + radius) {
|
|
||||||
for (int b = 0; b < NUM_AVATAR_BODY_BALLS; b++) {
|
|
||||||
glm::vec3 vectorFromBallToBigSphereCenter(_bodyBall[b].position - position);
|
|
||||||
float distanceToBigSphereCenter = glm::length(vectorFromBallToBigSphereCenter);
|
|
||||||
float combinedRadius = _bodyBall[b].radius + radius;
|
|
||||||
|
|
||||||
if (distanceToBigSphereCenter < combinedRadius) {
|
|
||||||
if (distanceToBigSphereCenter > 0.0) {
|
|
||||||
glm::vec3 directionVector = vectorFromBallToBigSphereCenter / distanceToBigSphereCenter;
|
|
||||||
|
|
||||||
float penetration = 1.0 - (distanceToBigSphereCenter / combinedRadius);
|
|
||||||
glm::vec3 collisionForce = vectorFromBallToBigSphereCenter * penetration;
|
|
||||||
|
|
||||||
_velocity += collisionForce * 40.0f * deltaTime;
|
|
||||||
_bodyBall[b].position = position + directionVector * combinedRadius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static TextRenderer* textRenderer() {
|
static TextRenderer* textRenderer() {
|
||||||
static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT);
|
static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT);
|
||||||
return renderer;
|
return renderer;
|
||||||
|
@ -786,6 +754,7 @@ void Avatar::loadData(QSettings* settings) {
|
||||||
_position.z = loadSetting(settings, "position_z", 0.0f);
|
_position.z = loadSetting(settings, "position_z", 0.0f);
|
||||||
|
|
||||||
_voxels.setVoxelURL(settings->value("voxelURL").toUrl());
|
_voxels.setVoxelURL(settings->value("voxelURL").toUrl());
|
||||||
|
_head.getBlendFace().setModelURL(settings->value("faceModelURL").toUrl());
|
||||||
|
|
||||||
_leanScale = loadSetting(settings, "leanScale", 0.05f);
|
_leanScale = loadSetting(settings, "leanScale", 0.05f);
|
||||||
|
|
||||||
|
@ -837,6 +806,7 @@ void Avatar::saveData(QSettings* set) {
|
||||||
set->setValue("position_z", _position.z);
|
set->setValue("position_z", _position.z);
|
||||||
|
|
||||||
set->setValue("voxelURL", _voxels.getVoxelURL());
|
set->setValue("voxelURL", _voxels.getVoxelURL());
|
||||||
|
set->setValue("faceModelURL", _head.getBlendFace().getModelURL());
|
||||||
|
|
||||||
set->setValue("leanScale", _leanScale);
|
set->setValue("leanScale", _leanScale);
|
||||||
set->setValue("scale", _newScale);
|
set->setValue("scale", _newScale);
|
||||||
|
|
|
@ -129,7 +129,7 @@ class Avatar : public AvatarData {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void sendAvatarVoxelURLMessage(const QUrl& url);
|
static void sendAvatarURLsMessage(const QUrl& voxelURL, const QUrl& faceURL);
|
||||||
|
|
||||||
Avatar(Node* owningNode = NULL);
|
Avatar(Node* owningNode = NULL);
|
||||||
~Avatar();
|
~Avatar();
|
||||||
|
@ -203,8 +203,6 @@ protected:
|
||||||
Hand _hand;
|
Hand _hand;
|
||||||
Skeleton _skeleton;
|
Skeleton _skeleton;
|
||||||
bool _ballSpringsInitialized;
|
bool _ballSpringsInitialized;
|
||||||
float _TEST_bigSphereRadius;
|
|
||||||
glm::vec3 _TEST_bigSpherePosition;
|
|
||||||
float _bodyYawDelta;
|
float _bodyYawDelta;
|
||||||
glm::vec3 _movedHandOffset;
|
glm::vec3 _movedHandOffset;
|
||||||
AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ];
|
AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ];
|
||||||
|
@ -234,7 +232,6 @@ protected:
|
||||||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||||
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||||
void updateCollisionWithSphere(glm::vec3 position, float radius, float deltaTime);
|
|
||||||
void updateBodyBalls(float deltaTime);
|
void updateBodyBalls(float deltaTime);
|
||||||
void updateArmIKAndConstraints(float deltaTime);
|
void updateArmIKAndConstraints(float deltaTime);
|
||||||
void setScale(const float scale);
|
void setScale(const float scale);
|
||||||
|
|
219
interface/src/avatar/BlendFace.cpp
Normal file
219
interface/src/avatar/BlendFace.cpp
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
//
|
||||||
|
// BlendFace.cpp
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 9/16/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
#include "BlendFace.h"
|
||||||
|
#include "Head.h"
|
||||||
|
|
||||||
|
using namespace fs;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
BlendFace::BlendFace(Head* owningHead) :
|
||||||
|
_owningHead(owningHead),
|
||||||
|
_modelReply(NULL),
|
||||||
|
_iboID(0)
|
||||||
|
{
|
||||||
|
// we may have been created in the network thread, but we live in the main thread
|
||||||
|
moveToThread(Application::getInstance()->thread());
|
||||||
|
}
|
||||||
|
|
||||||
|
BlendFace::~BlendFace() {
|
||||||
|
if (_iboID != 0) {
|
||||||
|
glDeleteBuffers(1, &_iboID);
|
||||||
|
glDeleteBuffers(1, &_vboID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlendFace::render(float alpha) {
|
||||||
|
if (_iboID == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
glTranslatef(_owningHead->getPosition().x, _owningHead->getPosition().y, _owningHead->getPosition().z);
|
||||||
|
glm::quat orientation = _owningHead->getOrientation();
|
||||||
|
glm::vec3 axis = glm::axis(orientation);
|
||||||
|
glRotatef(glm::angle(orientation), axis.x, axis.y, axis.z);
|
||||||
|
glTranslatef(0.0f, -0.025f, -0.025f); // temporary fudge factor until we have a better method of per-model positioning
|
||||||
|
const float MODEL_SCALE = 0.0006f;
|
||||||
|
glScalef(_owningHead->getScale() * MODEL_SCALE, _owningHead->getScale() * MODEL_SCALE,
|
||||||
|
-_owningHead->getScale() * MODEL_SCALE);
|
||||||
|
|
||||||
|
glColor4f(1.0f, 1.0f, 1.0f, alpha);
|
||||||
|
|
||||||
|
// start with the base
|
||||||
|
int vertexCount = _geometry.vertices.size();
|
||||||
|
_blendedVertices.resize(vertexCount);
|
||||||
|
memcpy(_blendedVertices.data(), _geometry.vertices.constData(), vertexCount * sizeof(glm::vec3));
|
||||||
|
|
||||||
|
// blend in each coefficient
|
||||||
|
const vector<float>& coefficients = _owningHead->getBlendshapeCoefficients();
|
||||||
|
for (int i = 0; i < coefficients.size(); i++) {
|
||||||
|
float coefficient = coefficients[i];
|
||||||
|
if (coefficient == 0.0f || i >= _geometry.blendshapes.size() || _geometry.blendshapes[i].vertices.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const glm::vec3* source = _geometry.blendshapes[i].vertices.constData();
|
||||||
|
for (const int* index = _geometry.blendshapes[i].indices.constData(),
|
||||||
|
*end = index + _geometry.blendshapes[i].indices.size(); index != end; index++, source++) {
|
||||||
|
_blendedVertices[*index] += *source * coefficient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the blended vertices
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData());
|
||||||
|
|
||||||
|
// tell OpenGL where to find vertex information
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glVertexPointer(3, GL_FLOAT, 0, 0);
|
||||||
|
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID);
|
||||||
|
glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, _geometry.quadIndices.size(), GL_UNSIGNED_INT, 0);
|
||||||
|
glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, _geometry.triangleIndices.size(), GL_UNSIGNED_INT,
|
||||||
|
(void*)(_geometry.quadIndices.size() * sizeof(int)));
|
||||||
|
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
|
|
||||||
|
// deactivate vertex arrays after drawing
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
|
||||||
|
// bind with 0 to switch back to normal operation
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlendFace::setModelURL(const QUrl& url) {
|
||||||
|
// don't restart the download if it's the same URL
|
||||||
|
if (_modelURL == url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancel any current download
|
||||||
|
if (_modelReply != 0) {
|
||||||
|
delete _modelReply;
|
||||||
|
_modelReply = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the current geometry, if any
|
||||||
|
setGeometry(FBXGeometry());
|
||||||
|
|
||||||
|
// remember the URL
|
||||||
|
_modelURL = url;
|
||||||
|
|
||||||
|
// load the URL data asynchronously
|
||||||
|
if (!url.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_modelReply = Application::getInstance()->getNetworkAccessManager()->get(QNetworkRequest(url));
|
||||||
|
connect(_modelReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleModelDownloadProgress(qint64,qint64)));
|
||||||
|
connect(_modelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleModelReplyError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 createVec3(const fsVector3f& vector) {
|
||||||
|
return glm::vec3(vector.x, vector.y, vector.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlendFace::setRig(const fsMsgRig& rig) {
|
||||||
|
// convert to FBX geometry
|
||||||
|
FBXGeometry geometry;
|
||||||
|
|
||||||
|
for (vector<fsVector4i>::const_iterator it = rig.mesh().m_quads.begin(), end = rig.mesh().m_quads.end(); it != end; it++) {
|
||||||
|
geometry.quadIndices.append(it->x);
|
||||||
|
geometry.quadIndices.append(it->y);
|
||||||
|
geometry.quadIndices.append(it->z);
|
||||||
|
geometry.quadIndices.append(it->w);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (vector<fsVector3i>::const_iterator it = rig.mesh().m_tris.begin(), end = rig.mesh().m_tris.end(); it != end; it++) {
|
||||||
|
geometry.triangleIndices.append(it->x);
|
||||||
|
geometry.triangleIndices.append(it->y);
|
||||||
|
geometry.triangleIndices.append(it->z);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (vector<fsVector3f>::const_iterator it = rig.mesh().m_vertex_data.m_vertices.begin(),
|
||||||
|
end = rig.mesh().m_vertex_data.m_vertices.end(); it != end; it++) {
|
||||||
|
geometry.vertices.append(glm::vec3(it->x, it->y, it->z));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (vector<fsVertexData>::const_iterator it = rig.blendshapes().begin(), end = rig.blendshapes().end(); it != end; it++) {
|
||||||
|
FBXBlendshape blendshape;
|
||||||
|
for (int i = 0, n = it->m_vertices.size(); i < n; i++) {
|
||||||
|
// subtract the base vertex position; we want the deltas
|
||||||
|
blendshape.vertices.append(createVec3(it->m_vertices[i]) - geometry.vertices[i]);
|
||||||
|
blendshape.indices.append(i);
|
||||||
|
}
|
||||||
|
geometry.blendshapes.append(blendshape);
|
||||||
|
}
|
||||||
|
|
||||||
|
setGeometry(geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlendFace::handleModelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||||
|
if (bytesReceived < bytesTotal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray entirety = _modelReply->readAll();
|
||||||
|
_modelReply->disconnect(this);
|
||||||
|
_modelReply->deleteLater();
|
||||||
|
_modelReply = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setGeometry(extractFBXGeometry(parseFBX(entirety)));
|
||||||
|
|
||||||
|
} catch (const QString& error) {
|
||||||
|
qDebug() << error << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlendFace::handleModelReplyError() {
|
||||||
|
qDebug("%s\n", _modelReply->errorString().toLocal8Bit().constData());
|
||||||
|
|
||||||
|
_modelReply->disconnect(this);
|
||||||
|
_modelReply->deleteLater();
|
||||||
|
_modelReply = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlendFace::setGeometry(const FBXGeometry& geometry) {
|
||||||
|
if (geometry.vertices.isEmpty()) {
|
||||||
|
// clear any existing geometry
|
||||||
|
if (_iboID != 0) {
|
||||||
|
glDeleteBuffers(1, &_iboID);
|
||||||
|
glDeleteBuffers(1, &_vboID);
|
||||||
|
_iboID = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_iboID == 0) {
|
||||||
|
glGenBuffers(1, &_iboID);
|
||||||
|
glGenBuffers(1, &_vboID);
|
||||||
|
}
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iboID);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (geometry.quadIndices.size() + geometry.triangleIndices.size()) * sizeof(int),
|
||||||
|
NULL, GL_STATIC_DRAW);
|
||||||
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, geometry.quadIndices.size() * sizeof(int), geometry.quadIndices.constData());
|
||||||
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, geometry.quadIndices.size() * sizeof(int),
|
||||||
|
geometry.triangleIndices.size() * sizeof(int), geometry.triangleIndices.constData());
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, geometry.vertices.size() * sizeof(glm::vec3), NULL, GL_DYNAMIC_DRAW);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
_geometry = geometry;
|
||||||
|
}
|
64
interface/src/avatar/BlendFace.h
Normal file
64
interface/src/avatar/BlendFace.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// BlendFace.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 9/16/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __interface__BlendFace__
|
||||||
|
#define __interface__BlendFace__
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include <fsbinarystream.h>
|
||||||
|
|
||||||
|
#include "InterfaceConfig.h"
|
||||||
|
#include "renderer/FBXReader.h"
|
||||||
|
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
class Head;
|
||||||
|
|
||||||
|
/// A face formed from a linear mix of blendshapes according to a set of coefficients.
|
||||||
|
class BlendFace : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
BlendFace(Head* owningHead);
|
||||||
|
~BlendFace();
|
||||||
|
|
||||||
|
bool render(float alpha);
|
||||||
|
|
||||||
|
Q_INVOKABLE void setModelURL(const QUrl& url);
|
||||||
|
const QUrl& getModelURL() const { return _modelURL; }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void setRig(const fs::fsMsgRig& rig);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void handleModelDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
void handleModelReplyError();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void setGeometry(const FBXGeometry& geometry);
|
||||||
|
|
||||||
|
Head* _owningHead;
|
||||||
|
|
||||||
|
QUrl _modelURL;
|
||||||
|
|
||||||
|
QNetworkReply* _modelReply;
|
||||||
|
|
||||||
|
GLuint _iboID;
|
||||||
|
GLuint _vboID;
|
||||||
|
|
||||||
|
FBXGeometry _geometry;
|
||||||
|
QVector<glm::vec3> _blendedVertices;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* defined(__interface__BlendFace__) */
|
|
@ -87,7 +87,8 @@ Head::Head(Avatar* owningAvatar) :
|
||||||
_cameraFollowsHead(false),
|
_cameraFollowsHead(false),
|
||||||
_cameraFollowHeadRate(0.0f),
|
_cameraFollowHeadRate(0.0f),
|
||||||
_face(this),
|
_face(this),
|
||||||
_perlinFace(this)
|
_perlinFace(this),
|
||||||
|
_blendFace(this)
|
||||||
{
|
{
|
||||||
if (USING_PHYSICAL_MOHAWK) {
|
if (USING_PHYSICAL_MOHAWK) {
|
||||||
resetHairPhysics();
|
resetHairPhysics();
|
||||||
|
@ -159,6 +160,7 @@ void Head::simulate(float deltaTime, bool isMine, float gyroCameraSensitivity) {
|
||||||
_averageLoudness = faceshift->getMouthSize() * faceshift->getMouthSize() * MOUTH_SIZE_SCALE;
|
_averageLoudness = faceshift->getMouthSize() * faceshift->getMouthSize() * MOUTH_SIZE_SCALE;
|
||||||
const float BROW_HEIGHT_SCALE = 0.005f;
|
const float BROW_HEIGHT_SCALE = 0.005f;
|
||||||
_browAudioLift = faceshift->getBrowUpCenter() * BROW_HEIGHT_SCALE;
|
_browAudioLift = faceshift->getBrowUpCenter() * BROW_HEIGHT_SCALE;
|
||||||
|
_blendshapeCoefficients = faceshift->getBlendshapeCoefficients();
|
||||||
|
|
||||||
} else if (!_isFaceshiftConnected) {
|
} else if (!_isFaceshiftConnected) {
|
||||||
// Update eye saccades
|
// Update eye saccades
|
||||||
|
@ -325,7 +327,7 @@ void Head::calculateGeometry() {
|
||||||
void Head::render(float alpha, bool isMine) {
|
void Head::render(float alpha, bool isMine) {
|
||||||
_renderAlpha = alpha;
|
_renderAlpha = alpha;
|
||||||
|
|
||||||
if (!_face.render(alpha)) {
|
if (!(_face.render(alpha) || _blendFace.render(alpha))) {
|
||||||
calculateGeometry();
|
calculateGeometry();
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
#include <VoxelConstants.h>
|
#include <VoxelConstants.h>
|
||||||
|
|
||||||
#include "BendyLine.h"
|
#include "BendyLine.h"
|
||||||
|
#include "BlendFace.h"
|
||||||
#include "Face.h"
|
#include "Face.h"
|
||||||
#include "PerlinFace.h"
|
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
|
#include "PerlinFace.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "devices/SerialInterface.h"
|
#include "devices/SerialInterface.h"
|
||||||
|
|
||||||
|
@ -71,6 +72,7 @@ public:
|
||||||
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||||
|
|
||||||
Face& getFace() { return _face; }
|
Face& getFace() { return _face; }
|
||||||
|
BlendFace& getBlendFace() { return _blendFace; }
|
||||||
|
|
||||||
const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
|
const bool getReturnToCenter() const { return _returnHeadToCenter; } // Do you want head to try to return to center (depends on interface detected)
|
||||||
float getAverageLoudness() const { return _averageLoudness; }
|
float getAverageLoudness() const { return _averageLoudness; }
|
||||||
|
@ -128,6 +130,7 @@ private:
|
||||||
float _cameraFollowHeadRate;
|
float _cameraFollowHeadRate;
|
||||||
Face _face;
|
Face _face;
|
||||||
PerlinFace _perlinFace;
|
PerlinFace _perlinFace;
|
||||||
|
BlendFace _blendFace;
|
||||||
|
|
||||||
static ProgramObject _irisProgram;
|
static ProgramObject _irisProgram;
|
||||||
static GLuint _irisTextureID;
|
static GLuint _irisTextureID;
|
||||||
|
|
|
@ -59,6 +59,14 @@ MyAvatar::MyAvatar(Node* owningNode) :
|
||||||
_collisionRadius = _height * COLLISION_RADIUS_SCALE;
|
_collisionRadius = _height * COLLISION_RADIUS_SCALE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyAvatar::init() {
|
||||||
|
Avatar::init();
|
||||||
|
|
||||||
|
// when we receive a Faceshift rig, apply it to our own blend face
|
||||||
|
_head.getBlendFace().connect(Application::getInstance()->getFaceshift(), SIGNAL(rigReceived(fs::fsMsgRig)),
|
||||||
|
SLOT(setRig(fs::fsMsgRig)));
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::reset() {
|
void MyAvatar::reset() {
|
||||||
_head.reset();
|
_head.reset();
|
||||||
_hand.reset();
|
_hand.reset();
|
||||||
|
@ -186,11 +194,6 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCam
|
||||||
// update body balls
|
// update body balls
|
||||||
updateBodyBalls(deltaTime);
|
updateBodyBalls(deltaTime);
|
||||||
|
|
||||||
// test for avatar collision response with the big sphere
|
|
||||||
if (usingBigSphereCollisionTest && _isCollisionsOn) {
|
|
||||||
updateCollisionWithSphere(_TEST_bigSpherePosition, _TEST_bigSphereRadius, deltaTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add thrust to velocity
|
// add thrust to velocity
|
||||||
_velocity += _thrust * deltaTime;
|
_velocity += _thrust * deltaTime;
|
||||||
|
|
||||||
|
@ -414,16 +417,6 @@ static TextRenderer* textRenderer() {
|
||||||
|
|
||||||
void MyAvatar::render(bool lookingInMirror, bool renderAvatarBalls) {
|
void MyAvatar::render(bool lookingInMirror, bool renderAvatarBalls) {
|
||||||
|
|
||||||
if (usingBigSphereCollisionTest) {
|
|
||||||
// show TEST big sphere
|
|
||||||
glColor4f(0.5f, 0.6f, 0.8f, 0.7);
|
|
||||||
glPushMatrix();
|
|
||||||
glTranslatef(_TEST_bigSpherePosition.x, _TEST_bigSpherePosition.y, _TEST_bigSpherePosition.z);
|
|
||||||
glScalef(_TEST_bigSphereRadius, _TEST_bigSphereRadius, _TEST_bigSphereRadius);
|
|
||||||
glutSolidSphere(1, 20, 20);
|
|
||||||
glPopMatrix();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Application::getInstance()->getAvatar()->getHand().isRaveGloveActive()) {
|
if (Application::getInstance()->getAvatar()->getHand().isRaveGloveActive()) {
|
||||||
_hand.setRaveLights(RAVE_LIGHTS_AVATAR);
|
_hand.setRaveLights(RAVE_LIGHTS_AVATAR);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ class MyAvatar : public Avatar {
|
||||||
public:
|
public:
|
||||||
MyAvatar(Node* owningNode = NULL);
|
MyAvatar(Node* owningNode = NULL);
|
||||||
|
|
||||||
|
void init();
|
||||||
void reset();
|
void reset();
|
||||||
void simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity);
|
void simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity);
|
||||||
void updateFromGyrosAndOrWebcam(bool gyroLook, float pitchFromTouch);
|
void updateFromGyrosAndOrWebcam(bool gyroLook, float pitchFromTouch);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include "Faceshift.h"
|
#include "Faceshift.h"
|
||||||
|
#include "Menu.h"
|
||||||
|
|
||||||
using namespace fs;
|
using namespace fs;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -24,29 +25,17 @@ Faceshift::Faceshift() :
|
||||||
_eyeGazeLeftYaw(0.0f),
|
_eyeGazeLeftYaw(0.0f),
|
||||||
_eyeGazeRightPitch(0.0f),
|
_eyeGazeRightPitch(0.0f),
|
||||||
_eyeGazeRightYaw(0.0f),
|
_eyeGazeRightYaw(0.0f),
|
||||||
_leftBlink(0.0f),
|
|
||||||
_rightBlink(0.0f),
|
|
||||||
_leftEyeOpen(0.0f),
|
|
||||||
_rightEyeOpen(0.0f),
|
|
||||||
_leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes
|
_leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes
|
||||||
_rightBlinkIndex(1),
|
_rightBlinkIndex(1),
|
||||||
_leftEyeOpenIndex(8),
|
_leftEyeOpenIndex(8),
|
||||||
_rightEyeOpenIndex(9),
|
_rightEyeOpenIndex(9),
|
||||||
_browDownLeft(0.0f),
|
_browDownLeftIndex(14),
|
||||||
_browDownRight(0.0f),
|
_browDownRightIndex(15),
|
||||||
_browUpCenter(0.0f),
|
|
||||||
_browUpLeft(0.0f),
|
|
||||||
_browUpRight(0.0f),
|
|
||||||
_browDownLeftIndex(-1),
|
|
||||||
_browDownRightIndex(-1),
|
|
||||||
_browUpCenterIndex(16),
|
_browUpCenterIndex(16),
|
||||||
_browUpLeftIndex(-1),
|
_browUpLeftIndex(17),
|
||||||
_browUpRightIndex(-1),
|
_browUpRightIndex(18),
|
||||||
_mouthSize(0.0f),
|
_mouthSmileLeftIndex(28),
|
||||||
_mouthSmileLeft(0),
|
_mouthSmileRightIndex(29),
|
||||||
_mouthSmileRight(0),
|
|
||||||
_mouthSmileLeftIndex(-1),
|
|
||||||
_mouthSmileRightIndex(0),
|
|
||||||
_jawOpenIndex(21),
|
_jawOpenIndex(21),
|
||||||
_longTermAverageEyePitch(0.0f),
|
_longTermAverageEyePitch(0.0f),
|
||||||
_longTermAverageEyeYaw(0.0f),
|
_longTermAverageEyeYaw(0.0f),
|
||||||
|
@ -98,6 +87,17 @@ void Faceshift::setTCPEnabled(bool enabled) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Faceshift::setUsingRig(bool usingRig) {
|
||||||
|
if (usingRig && _tcpSocket.state() == QAbstractSocket::ConnectedState) {
|
||||||
|
string message;
|
||||||
|
fsBinaryStream::encode_message(message, fsMsgSendRig());
|
||||||
|
send(message);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
emit rigReceived(fsMsgRig());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Faceshift::connectSocket() {
|
void Faceshift::connectSocket() {
|
||||||
if (_tcpEnabled) {
|
if (_tcpEnabled) {
|
||||||
qDebug("Faceshift: Connecting...\n");
|
qDebug("Faceshift: Connecting...\n");
|
||||||
|
@ -114,6 +114,11 @@ void Faceshift::noteConnected() {
|
||||||
string message;
|
string message;
|
||||||
fsBinaryStream::encode_message(message, fsMsgSendBlendshapeNames());
|
fsBinaryStream::encode_message(message, fsMsgSendBlendshapeNames());
|
||||||
send(message);
|
send(message);
|
||||||
|
|
||||||
|
// if using faceshift rig, request it
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::UseFaceshiftRig)) {
|
||||||
|
setUsingRig(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Faceshift::noteError(QAbstractSocket::SocketError error) {
|
void Faceshift::noteError(QAbstractSocket::SocketError error) {
|
||||||
|
@ -138,6 +143,10 @@ void Faceshift::readFromSocket() {
|
||||||
receive(_tcpSocket.readAll());
|
receive(_tcpSocket.readAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Faceshift::getBlendshapeCoefficient(int index) const {
|
||||||
|
return (index >= 0 && index < _blendshapeCoefficients.size()) ? _blendshapeCoefficients[index] : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
void Faceshift::send(const std::string& message) {
|
void Faceshift::send(const std::string& message) {
|
||||||
_tcpSocket.write(message.data(), message.size());
|
_tcpSocket.write(message.data(), message.size());
|
||||||
}
|
}
|
||||||
|
@ -159,43 +168,7 @@ void Faceshift::receive(const QByteArray& buffer) {
|
||||||
_eyeGazeLeftYaw = data.m_eyeGazeLeftYaw;
|
_eyeGazeLeftYaw = data.m_eyeGazeLeftYaw;
|
||||||
_eyeGazeRightPitch = -data.m_eyeGazeRightPitch;
|
_eyeGazeRightPitch = -data.m_eyeGazeRightPitch;
|
||||||
_eyeGazeRightYaw = data.m_eyeGazeRightYaw;
|
_eyeGazeRightYaw = data.m_eyeGazeRightYaw;
|
||||||
|
_blendshapeCoefficients = data.m_coeffs;
|
||||||
if (_leftBlinkIndex != -1) {
|
|
||||||
_leftBlink = data.m_coeffs[_leftBlinkIndex];
|
|
||||||
}
|
|
||||||
if (_rightBlinkIndex != -1) {
|
|
||||||
_rightBlink = data.m_coeffs[_rightBlinkIndex];
|
|
||||||
}
|
|
||||||
if (_leftEyeOpenIndex != -1) {
|
|
||||||
_leftEyeOpen = data.m_coeffs[_leftEyeOpenIndex];
|
|
||||||
}
|
|
||||||
if (_rightEyeOpenIndex != -1) {
|
|
||||||
_rightEyeOpen = data.m_coeffs[_rightEyeOpenIndex];
|
|
||||||
}
|
|
||||||
if (_browDownLeftIndex != -1) {
|
|
||||||
_browDownLeft = data.m_coeffs[_browDownLeftIndex];
|
|
||||||
}
|
|
||||||
if (_browDownRightIndex != -1) {
|
|
||||||
_browDownRight = data.m_coeffs[_browDownRightIndex];
|
|
||||||
}
|
|
||||||
if (_browUpCenterIndex != -1) {
|
|
||||||
_browUpCenter = data.m_coeffs[_browUpCenterIndex];
|
|
||||||
}
|
|
||||||
if (_browUpLeftIndex != -1) {
|
|
||||||
_browUpLeft = data.m_coeffs[_browUpLeftIndex];
|
|
||||||
}
|
|
||||||
if (_browUpRightIndex != -1) {
|
|
||||||
_browUpRight = data.m_coeffs[_browUpRightIndex];
|
|
||||||
}
|
|
||||||
if (_jawOpenIndex != -1) {
|
|
||||||
_mouthSize = data.m_coeffs[_jawOpenIndex];
|
|
||||||
}
|
|
||||||
if (_mouthSmileLeftIndex != -1) {
|
|
||||||
_mouthSmileLeft = data.m_coeffs[_mouthSmileLeftIndex];
|
|
||||||
}
|
|
||||||
if (_mouthSmileRightIndex != -1) {
|
|
||||||
_mouthSmileRight = data.m_coeffs[_mouthSmileRightIndex];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -208,10 +181,10 @@ void Faceshift::receive(const QByteArray& buffer) {
|
||||||
} else if (names[i] == "EyeBlink_R") {
|
} else if (names[i] == "EyeBlink_R") {
|
||||||
_rightBlinkIndex = i;
|
_rightBlinkIndex = i;
|
||||||
|
|
||||||
}else if (names[i] == "EyeOpen_L") {
|
} else if (names[i] == "EyeOpen_L") {
|
||||||
_leftEyeOpenIndex = i;
|
_leftEyeOpenIndex = i;
|
||||||
|
|
||||||
}else if (names[i] == "EyeOpen_R") {
|
} else if (names[i] == "EyeOpen_R") {
|
||||||
_rightEyeOpenIndex = i;
|
_rightEyeOpenIndex = i;
|
||||||
|
|
||||||
} else if (names[i] == "BrowsD_L") {
|
} else if (names[i] == "BrowsD_L") {
|
||||||
|
@ -237,11 +210,15 @@ void Faceshift::receive(const QByteArray& buffer) {
|
||||||
|
|
||||||
} else if (names[i] == "MouthSmile_R") {
|
} else if (names[i] == "MouthSmile_R") {
|
||||||
_mouthSmileRightIndex = i;
|
_mouthSmileRightIndex = i;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case fsMsg::MSG_OUT_RIG: {
|
||||||
|
fsMsgRig* rig = static_cast<fsMsgRig*>(msg.get());
|
||||||
|
emit rigReceived(*rig);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#ifndef __interface__Faceshift__
|
#ifndef __interface__Faceshift__
|
||||||
#define __interface__Faceshift__
|
#define __interface__Faceshift__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
#include <QUdpSocket>
|
#include <QUdpSocket>
|
||||||
|
|
||||||
|
@ -39,27 +41,34 @@ public:
|
||||||
float getEstimatedEyePitch() const { return _estimatedEyePitch; }
|
float getEstimatedEyePitch() const { return _estimatedEyePitch; }
|
||||||
float getEstimatedEyeYaw() const { return _estimatedEyeYaw; }
|
float getEstimatedEyeYaw() const { return _estimatedEyeYaw; }
|
||||||
|
|
||||||
float getLeftBlink() const { return _leftBlink; }
|
const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||||
float getRightBlink() const { return _rightBlink; }
|
|
||||||
float getLeftEyeOpen() const { return _leftEyeOpen; }
|
|
||||||
float getRightEyeOpen() const { return _rightEyeOpen; }
|
|
||||||
|
|
||||||
float getBrowDownLeft() const { return _browDownLeft; }
|
float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); }
|
||||||
float getBrowDownRight() const { return _browDownRight; }
|
float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); }
|
||||||
float getBrowUpCenter() const { return _browUpCenter; }
|
float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); }
|
||||||
float getBrowUpLeft() const { return _browUpLeft; }
|
float getRightEyeOpen() const { return getBlendshapeCoefficient(_rightEyeOpenIndex); }
|
||||||
float getBrowUpRight() const { return _browUpRight; }
|
|
||||||
|
|
||||||
float getMouthSize() const { return _mouthSize; }
|
float getBrowDownLeft() const { return getBlendshapeCoefficient(_browDownLeftIndex); }
|
||||||
float getMouthSmileLeft() const { return _mouthSmileLeft; }
|
float getBrowDownRight() const { return getBlendshapeCoefficient(_browDownRightIndex); }
|
||||||
float getMouthSmileRight() const { return _mouthSmileRight; }
|
float getBrowUpCenter() const { return getBlendshapeCoefficient(_browUpCenterIndex); }
|
||||||
|
float getBrowUpLeft() const { return getBlendshapeCoefficient(_browUpLeftIndex); }
|
||||||
|
float getBrowUpRight() const { return getBlendshapeCoefficient(_browUpRightIndex); }
|
||||||
|
|
||||||
|
float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); }
|
||||||
|
float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); }
|
||||||
|
float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); }
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void rigReceived(const fs::fsMsgRig& rig);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
void setTCPEnabled(bool enabled);
|
void setTCPEnabled(bool enabled);
|
||||||
|
void setUsingRig(bool usingRig);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
|
@ -71,6 +80,8 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
float getBlendshapeCoefficient(int index) const;
|
||||||
|
|
||||||
void send(const std::string& message);
|
void send(const std::string& message);
|
||||||
void receive(const QByteArray& buffer);
|
void receive(const QByteArray& buffer);
|
||||||
|
|
||||||
|
@ -90,10 +101,7 @@ private:
|
||||||
float _eyeGazeRightPitch;
|
float _eyeGazeRightPitch;
|
||||||
float _eyeGazeRightYaw;
|
float _eyeGazeRightYaw;
|
||||||
|
|
||||||
float _leftBlink;
|
std::vector<float> _blendshapeCoefficients;
|
||||||
float _rightBlink;
|
|
||||||
float _leftEyeOpen;
|
|
||||||
float _rightEyeOpen;
|
|
||||||
|
|
||||||
int _leftBlinkIndex;
|
int _leftBlinkIndex;
|
||||||
int _rightBlinkIndex;
|
int _rightBlinkIndex;
|
||||||
|
@ -101,24 +109,12 @@ private:
|
||||||
int _rightEyeOpenIndex;
|
int _rightEyeOpenIndex;
|
||||||
|
|
||||||
// Brows
|
// Brows
|
||||||
float _browDownLeft;
|
|
||||||
float _browDownRight;
|
|
||||||
float _browUpCenter;
|
|
||||||
float _browUpLeft;
|
|
||||||
float _browUpRight;
|
|
||||||
|
|
||||||
int _browDownLeftIndex;
|
int _browDownLeftIndex;
|
||||||
int _browDownRightIndex;
|
int _browDownRightIndex;
|
||||||
|
|
||||||
int _browUpCenterIndex;
|
int _browUpCenterIndex;
|
||||||
int _browUpLeftIndex;
|
int _browUpLeftIndex;
|
||||||
int _browUpRightIndex;
|
int _browUpRightIndex;
|
||||||
|
|
||||||
float _mouthSize;
|
|
||||||
|
|
||||||
float _mouthSmileLeft;
|
|
||||||
float _mouthSmileRight;
|
|
||||||
|
|
||||||
int _mouthSmileLeftIndex;
|
int _mouthSmileLeftIndex;
|
||||||
int _mouthSmileRightIndex;
|
int _mouthSmileRightIndex;
|
||||||
|
|
||||||
|
|
378
interface/src/renderer/FBXReader.cpp
Normal file
378
interface/src/renderer/FBXReader.cpp
Normal file
|
@ -0,0 +1,378 @@
|
||||||
|
//
|
||||||
|
// FBXReader.cpp
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 9/18/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QtEndian>
|
||||||
|
|
||||||
|
#include "FBXReader.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
FBXNode parseFBX(const QByteArray& data) {
|
||||||
|
QBuffer buffer(const_cast<QByteArray*>(&data));
|
||||||
|
buffer.open(QIODevice::ReadOnly);
|
||||||
|
return parseFBX(&buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T> QVariant readArray(QDataStream& in) {
|
||||||
|
quint32 arrayLength;
|
||||||
|
quint32 encoding;
|
||||||
|
quint32 compressedLength;
|
||||||
|
|
||||||
|
in >> arrayLength;
|
||||||
|
in >> encoding;
|
||||||
|
in >> compressedLength;
|
||||||
|
|
||||||
|
QVector<T> values;
|
||||||
|
const int DEFLATE_ENCODING = 1;
|
||||||
|
if (encoding == DEFLATE_ENCODING) {
|
||||||
|
// preface encoded data with uncompressed length
|
||||||
|
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
||||||
|
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
||||||
|
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
||||||
|
QByteArray uncompressed = qUncompress(compressed);
|
||||||
|
QDataStream uncompressedIn(uncompressed);
|
||||||
|
uncompressedIn.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch
|
||||||
|
for (int i = 0; i < arrayLength; i++) {
|
||||||
|
T value;
|
||||||
|
uncompressedIn >> value;
|
||||||
|
values.append(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < arrayLength; i++) {
|
||||||
|
T value;
|
||||||
|
in >> value;
|
||||||
|
values.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QVariant::fromValue(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant parseFBXProperty(QDataStream& in) {
|
||||||
|
char ch;
|
||||||
|
in.device()->getChar(&ch);
|
||||||
|
switch (ch) {
|
||||||
|
case 'Y': {
|
||||||
|
qint16 value;
|
||||||
|
in >> value;
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'C': {
|
||||||
|
bool value;
|
||||||
|
in >> value;
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'I': {
|
||||||
|
qint32 value;
|
||||||
|
in >> value;
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'F': {
|
||||||
|
float value;
|
||||||
|
in >> value;
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'D': {
|
||||||
|
double value;
|
||||||
|
in >> value;
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'L': {
|
||||||
|
qint64 value;
|
||||||
|
in >> value;
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'f': {
|
||||||
|
return readArray<float>(in);
|
||||||
|
}
|
||||||
|
case 'd': {
|
||||||
|
return readArray<double>(in);
|
||||||
|
}
|
||||||
|
case 'l': {
|
||||||
|
return readArray<qint64>(in);
|
||||||
|
}
|
||||||
|
case 'i': {
|
||||||
|
return readArray<qint32>(in);
|
||||||
|
}
|
||||||
|
case 'b': {
|
||||||
|
return readArray<bool>(in);
|
||||||
|
}
|
||||||
|
case 'S':
|
||||||
|
case 'R': {
|
||||||
|
quint32 length;
|
||||||
|
in >> length;
|
||||||
|
return QVariant::fromValue(in.device()->read(length));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw QString("Unknown property type: ") + ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FBXNode parseFBXNode(QDataStream& in) {
|
||||||
|
quint32 endOffset;
|
||||||
|
quint32 propertyCount;
|
||||||
|
quint32 propertyListLength;
|
||||||
|
quint8 nameLength;
|
||||||
|
|
||||||
|
in >> endOffset;
|
||||||
|
in >> propertyCount;
|
||||||
|
in >> propertyListLength;
|
||||||
|
in >> nameLength;
|
||||||
|
|
||||||
|
FBXNode node;
|
||||||
|
const int MIN_VALID_OFFSET = 40;
|
||||||
|
if (endOffset < MIN_VALID_OFFSET || nameLength == 0) {
|
||||||
|
// use a null name to indicate a null node
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
node.name = in.device()->read(nameLength);
|
||||||
|
|
||||||
|
for (int i = 0; i < propertyCount; i++) {
|
||||||
|
node.properties.append(parseFBXProperty(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (endOffset > in.device()->pos()) {
|
||||||
|
FBXNode child = parseFBXNode(in);
|
||||||
|
if (child.name.isNull()) {
|
||||||
|
return node;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
node.children.append(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
FBXNode parseFBX(QIODevice* device) {
|
||||||
|
QDataStream in(device);
|
||||||
|
in.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
in.setVersion(QDataStream::Qt_4_5); // for single/double precision switch
|
||||||
|
|
||||||
|
// see http://code.blender.org/index.php/2013/08/fbx-binary-file-format-specification/ for an explanation
|
||||||
|
// of the FBX format
|
||||||
|
|
||||||
|
// verify the prolog
|
||||||
|
const QByteArray EXPECTED_PROLOG = "Kaydara FBX Binary ";
|
||||||
|
if (device->read(EXPECTED_PROLOG.size()) != EXPECTED_PROLOG) {
|
||||||
|
throw QString("Invalid header.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip the rest of the header
|
||||||
|
const int HEADER_SIZE = 27;
|
||||||
|
in.skipRawData(HEADER_SIZE - EXPECTED_PROLOG.size());
|
||||||
|
|
||||||
|
// parse the top-level node
|
||||||
|
FBXNode top;
|
||||||
|
while (device->bytesAvailable()) {
|
||||||
|
FBXNode next = parseFBXNode(in);
|
||||||
|
if (next.name.isNull()) {
|
||||||
|
return top;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
top.children.append(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
|
||||||
|
QVector<glm::vec3> values;
|
||||||
|
for (const double* it = doubleVector.constData(), *end = it + doubleVector.size(); it != end; ) {
|
||||||
|
values.append(glm::vec3(*it++, *it++, *it++));
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* FACESHIFT_BLENDSHAPES[] = {
|
||||||
|
"EyeBlink_L",
|
||||||
|
"EyeBlink_R",
|
||||||
|
"EyeSquint_L",
|
||||||
|
"EyeSquint_R",
|
||||||
|
"EyeDown_L",
|
||||||
|
"EyeDown_R",
|
||||||
|
"EyeIn_L",
|
||||||
|
"EyeIn_R",
|
||||||
|
"EyeOpen_L",
|
||||||
|
"EyeOpen_R",
|
||||||
|
"EyeOut_L",
|
||||||
|
"EyeOut_R",
|
||||||
|
"EyeUp_L",
|
||||||
|
"EyeUp_R",
|
||||||
|
"BrowsD_L",
|
||||||
|
"BrowsD_R",
|
||||||
|
"BrowsU_C",
|
||||||
|
"BrowsU_L",
|
||||||
|
"BrowsU_R",
|
||||||
|
"JawFwd",
|
||||||
|
"JawLeft",
|
||||||
|
"JawOpen",
|
||||||
|
"JawChew",
|
||||||
|
"JawRight",
|
||||||
|
"MouthLeft",
|
||||||
|
"MouthRight",
|
||||||
|
"MouthFrown_L",
|
||||||
|
"MouthFrown_R",
|
||||||
|
"MouthSmile_L",
|
||||||
|
"MouthSmile_R",
|
||||||
|
"MouthDimple_L",
|
||||||
|
"MouthDimple_R",
|
||||||
|
"LipsStretch_L",
|
||||||
|
"LipsStretch_R",
|
||||||
|
"LipsUpperClose",
|
||||||
|
"LipsLowerClose",
|
||||||
|
"LipsUpperUp",
|
||||||
|
"LipsLowerDown",
|
||||||
|
"LipsUpperOpen",
|
||||||
|
"LipsLowerOpen",
|
||||||
|
"LipsFunnel",
|
||||||
|
"LipsPucker",
|
||||||
|
"ChinLowerRaise",
|
||||||
|
"ChinUpperRaise",
|
||||||
|
"Sneer",
|
||||||
|
"Puff",
|
||||||
|
"CheekSquint_L",
|
||||||
|
"CheekSquint_R",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
QHash<QByteArray, int> createBlendshapeMap() {
|
||||||
|
QHash<QByteArray, int> map;
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
QByteArray name = FACESHIFT_BLENDSHAPES[i];
|
||||||
|
if (name != "") {
|
||||||
|
map.insert(name, i);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FBXGeometry extractFBXGeometry(const FBXNode& node) {
|
||||||
|
QVector<FBXBlendshape> blendshapes;
|
||||||
|
QHash<qint64, FBXGeometry> meshMap;
|
||||||
|
qint64 blendshapeId = 0;
|
||||||
|
QHash<qint64, qint64> parentMap;
|
||||||
|
|
||||||
|
foreach (const FBXNode& child, node.children) {
|
||||||
|
if (child.name == "Objects") {
|
||||||
|
foreach (const FBXNode& object, child.children) {
|
||||||
|
if (object.name == "Geometry") {
|
||||||
|
if (object.properties.at(2) == "Mesh") {
|
||||||
|
FBXGeometry mesh;
|
||||||
|
|
||||||
|
QVector<glm::vec3> vertices;
|
||||||
|
QVector<int> polygonIndices;
|
||||||
|
foreach (const FBXNode& data, object.children) {
|
||||||
|
if (data.name == "Vertices") {
|
||||||
|
mesh.vertices = createVec3Vector(data.properties.at(0).value<QVector<double> >());
|
||||||
|
|
||||||
|
} else if (data.name == "PolygonVertexIndex") {
|
||||||
|
polygonIndices = data.properties.at(0).value<QVector<int> >();
|
||||||
|
|
||||||
|
} else if (data.name == "LayerElementNormal") {
|
||||||
|
foreach (const FBXNode& subdata, data.children) {
|
||||||
|
if (subdata.name == "Normals") {
|
||||||
|
mesh.normals = createVec3Vector(
|
||||||
|
subdata.properties.at(0).value<QVector<double> >());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// convert the polygons to quads and triangles
|
||||||
|
for (const int* beginIndex = polygonIndices.constData(), *end = beginIndex + polygonIndices.size();
|
||||||
|
beginIndex != end; ) {
|
||||||
|
const int* endIndex = beginIndex;
|
||||||
|
while (*endIndex++ >= 0);
|
||||||
|
|
||||||
|
if (endIndex - beginIndex == 4) {
|
||||||
|
mesh.quadIndices.append(*beginIndex++);
|
||||||
|
mesh.quadIndices.append(*beginIndex++);
|
||||||
|
mesh.quadIndices.append(*beginIndex++);
|
||||||
|
mesh.quadIndices.append(-*beginIndex++ - 1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (const int* nextIndex = beginIndex + 1;; ) {
|
||||||
|
mesh.triangleIndices.append(*beginIndex);
|
||||||
|
mesh.triangleIndices.append(*nextIndex++);
|
||||||
|
if (*nextIndex >= 0) {
|
||||||
|
mesh.triangleIndices.append(*nextIndex);
|
||||||
|
} else {
|
||||||
|
mesh.triangleIndices.append(-*nextIndex - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
beginIndex = endIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
meshMap.insert(object.properties.at(0).value<qint64>(), mesh);
|
||||||
|
|
||||||
|
} else { // object.properties.at(2) == "Shape"
|
||||||
|
FBXBlendshape blendshape;
|
||||||
|
foreach (const FBXNode& data, object.children) {
|
||||||
|
if (data.name == "Indexes") {
|
||||||
|
blendshape.indices = data.properties.at(0).value<QVector<int> >();
|
||||||
|
|
||||||
|
} else if (data.name == "Vertices") {
|
||||||
|
blendshape.vertices = createVec3Vector(data.properties.at(0).value<QVector<double> >());
|
||||||
|
|
||||||
|
} else if (data.name == "Normals") {
|
||||||
|
blendshape.normals = createVec3Vector(data.properties.at(0).value<QVector<double> >());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the name is followed by a null and some type info
|
||||||
|
QByteArray name = object.properties.at(1).toByteArray();
|
||||||
|
static QHash<QByteArray, int> blendshapeMap = createBlendshapeMap();
|
||||||
|
int index = blendshapeMap.value(name.left(name.indexOf('\0')));
|
||||||
|
blendshapes.resize(qMax(blendshapes.size(), index + 1));
|
||||||
|
blendshapes[index] = blendshape;
|
||||||
|
}
|
||||||
|
} else if (object.name == "Deformer" && object.properties.at(2) == "BlendShape") {
|
||||||
|
blendshapeId = object.properties.at(0).value<qint64>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (child.name == "Connections") {
|
||||||
|
foreach (const FBXNode& connection, child.children) {
|
||||||
|
if (connection.name == "C") {
|
||||||
|
parentMap.insert(connection.properties.at(1).value<qint64>(), connection.properties.at(2).value<qint64>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the mesh that owns the blendshape
|
||||||
|
FBXGeometry geometry;
|
||||||
|
if (meshMap.size() == 1) {
|
||||||
|
geometry = *meshMap.begin();
|
||||||
|
} else {
|
||||||
|
geometry = meshMap.take(parentMap.value(blendshapeId));
|
||||||
|
}
|
||||||
|
geometry.blendshapes = blendshapes;
|
||||||
|
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNode(const FBXNode& node, int indent) {
|
||||||
|
QByteArray spaces(indent, ' ');
|
||||||
|
qDebug("%s%s: ", spaces.data(), node.name.data());
|
||||||
|
foreach (const QVariant& property, node.properties) {
|
||||||
|
qDebug() << property;
|
||||||
|
}
|
||||||
|
qDebug() << "\n";
|
||||||
|
foreach (const FBXNode& child, node.children) {
|
||||||
|
printNode(child, indent + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
66
interface/src/renderer/FBXReader.h
Normal file
66
interface/src/renderer/FBXReader.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
//
|
||||||
|
// FBXReader.h
|
||||||
|
// interface
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 9/18/13.
|
||||||
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __interface__FBXReader__
|
||||||
|
#define __interface__FBXReader__
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
class QIODevice;
|
||||||
|
|
||||||
|
class FBXNode;
|
||||||
|
|
||||||
|
typedef QList<FBXNode> FBXNodeList;
|
||||||
|
|
||||||
|
/// A node within an FBX document.
|
||||||
|
class FBXNode {
|
||||||
|
public:
|
||||||
|
|
||||||
|
QByteArray name;
|
||||||
|
QVariantList properties;
|
||||||
|
FBXNodeList children;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A single blendshape extracted from an FBX document.
|
||||||
|
class FBXBlendshape {
|
||||||
|
public:
|
||||||
|
|
||||||
|
QVector<int> indices;
|
||||||
|
QVector<glm::vec3> vertices;
|
||||||
|
QVector<glm::vec3> normals;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Base geometry with blendshapes mapped by name.
|
||||||
|
class FBXGeometry {
|
||||||
|
public:
|
||||||
|
|
||||||
|
QVector<int> quadIndices;
|
||||||
|
QVector<int> triangleIndices;
|
||||||
|
QVector<glm::vec3> vertices;
|
||||||
|
QVector<glm::vec3> normals;
|
||||||
|
|
||||||
|
QVector<FBXBlendshape> blendshapes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Parses the input from the supplied data as an FBX file.
|
||||||
|
/// \exception QString if an error occurs in parsing
|
||||||
|
FBXNode parseFBX(const QByteArray& data);
|
||||||
|
|
||||||
|
/// Parses the input from the supplied device as an FBX file.
|
||||||
|
/// \exception QString if an error occurs in parsing
|
||||||
|
FBXNode parseFBX(QIODevice* device);
|
||||||
|
|
||||||
|
/// Extracts the geometry from a parsed FBX node.
|
||||||
|
FBXGeometry extractFBXGeometry(const FBXNode& node);
|
||||||
|
|
||||||
|
void printNode(const FBXNode& node, int indent = 0);
|
||||||
|
|
||||||
|
#endif /* defined(__interface__FBXReader__) */
|
|
@ -204,6 +204,11 @@ int AvatarData::getBroadcastData(unsigned char* destinationBuffer) {
|
||||||
|
|
||||||
memcpy(destinationBuffer, &_headData->_browAudioLift, sizeof(float));
|
memcpy(destinationBuffer, &_headData->_browAudioLift, sizeof(float));
|
||||||
destinationBuffer += sizeof(float);
|
destinationBuffer += sizeof(float);
|
||||||
|
|
||||||
|
*destinationBuffer++ = _headData->_blendshapeCoefficients.size();
|
||||||
|
memcpy(destinationBuffer, _headData->_blendshapeCoefficients.data(),
|
||||||
|
_headData->_blendshapeCoefficients.size() * sizeof(float));
|
||||||
|
destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
|
||||||
}
|
}
|
||||||
|
|
||||||
// leap hand data
|
// leap hand data
|
||||||
|
@ -334,6 +339,11 @@ int AvatarData::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||||
|
|
||||||
memcpy(&_headData->_browAudioLift, sourceBuffer, sizeof(float));
|
memcpy(&_headData->_browAudioLift, sourceBuffer, sizeof(float));
|
||||||
sourceBuffer += sizeof(float);
|
sourceBuffer += sizeof(float);
|
||||||
|
|
||||||
|
_headData->_blendshapeCoefficients.resize(*sourceBuffer++);
|
||||||
|
memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer,
|
||||||
|
_headData->_blendshapeCoefficients.size() * sizeof(float));
|
||||||
|
sourceBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
|
||||||
}
|
}
|
||||||
|
|
||||||
// leap hand data
|
// leap hand data
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#define __hifi__HeadData__
|
#define __hifi__HeadData__
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
@ -43,6 +44,8 @@ public:
|
||||||
|
|
||||||
void setAudioLoudness(float audioLoudness) { _audioLoudness = audioLoudness; }
|
void setAudioLoudness(float audioLoudness) { _audioLoudness = audioLoudness; }
|
||||||
|
|
||||||
|
const std::vector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||||
|
|
||||||
void addYaw(float yaw);
|
void addYaw(float yaw);
|
||||||
void addPitch(float pitch);
|
void addPitch(float pitch);
|
||||||
void addRoll(float roll);
|
void addRoll(float roll);
|
||||||
|
@ -52,6 +55,7 @@ public:
|
||||||
void setLookAtPosition(const glm::vec3& lookAtPosition) { _lookAtPosition = lookAtPosition; }
|
void setLookAtPosition(const glm::vec3& lookAtPosition) { _lookAtPosition = lookAtPosition; }
|
||||||
|
|
||||||
friend class AvatarData;
|
friend class AvatarData;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float _yaw;
|
float _yaw;
|
||||||
float _pitch;
|
float _pitch;
|
||||||
|
@ -65,7 +69,9 @@ protected:
|
||||||
float _rightEyeBlink;
|
float _rightEyeBlink;
|
||||||
float _averageLoudness;
|
float _averageLoudness;
|
||||||
float _browAudioLift;
|
float _browAudioLift;
|
||||||
|
std::vector<float> _blendshapeCoefficients;
|
||||||
AvatarData* _owningAvatar;
|
AvatarData* _owningAvatar;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// privatize copy ctor and assignment operator so copies of this object cannot be made
|
// privatize copy ctor and assignment operator so copies of this object cannot be made
|
||||||
HeadData(const HeadData&);
|
HeadData(const HeadData&);
|
||||||
|
|
|
@ -20,7 +20,10 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
case PACKET_TYPE_HEAD_DATA:
|
case PACKET_TYPE_HEAD_DATA:
|
||||||
return 6;
|
return 7;
|
||||||
|
|
||||||
|
case PACKET_TYPE_AVATAR_URLS:
|
||||||
|
return 1;
|
||||||
|
|
||||||
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
case PACKET_TYPE_AVATAR_FACE_VIDEO:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -29,7 +29,7 @@ const PACKET_TYPE PACKET_TYPE_ERASE_VOXEL = 'E';
|
||||||
const PACKET_TYPE PACKET_TYPE_VOXEL_DATA = 'V';
|
const PACKET_TYPE PACKET_TYPE_VOXEL_DATA = 'V';
|
||||||
const PACKET_TYPE PACKET_TYPE_VOXEL_DATA_MONOCHROME = 'v';
|
const PACKET_TYPE PACKET_TYPE_VOXEL_DATA_MONOCHROME = 'v';
|
||||||
const PACKET_TYPE PACKET_TYPE_BULK_AVATAR_DATA = 'X';
|
const PACKET_TYPE PACKET_TYPE_BULK_AVATAR_DATA = 'X';
|
||||||
const PACKET_TYPE PACKET_TYPE_AVATAR_VOXEL_URL = 'U';
|
const PACKET_TYPE PACKET_TYPE_AVATAR_URLS = 'U';
|
||||||
const PACKET_TYPE PACKET_TYPE_AVATAR_FACE_VIDEO = 'F';
|
const PACKET_TYPE PACKET_TYPE_AVATAR_FACE_VIDEO = 'F';
|
||||||
const PACKET_TYPE PACKET_TYPE_TRANSMITTER_DATA_V2 = 'T';
|
const PACKET_TYPE PACKET_TYPE_TRANSMITTER_DATA_V2 = 'T';
|
||||||
const PACKET_TYPE PACKET_TYPE_ENVIRONMENT_DATA = 'e';
|
const PACKET_TYPE PACKET_TYPE_ENVIRONMENT_DATA = 'e';
|
||||||
|
|
|
@ -52,7 +52,6 @@ VoxelServer::VoxelServer(Assignment::Command command, Assignment::Location locat
|
||||||
_serverTree(true) {
|
_serverTree(true) {
|
||||||
_argc = 0;
|
_argc = 0;
|
||||||
_argv = NULL;
|
_argv = NULL;
|
||||||
_dontKillOnMissingDomain = false;
|
|
||||||
|
|
||||||
_packetsPerClientPerInterval = 10;
|
_packetsPerClientPerInterval = 10;
|
||||||
_wantVoxelPersist = true;
|
_wantVoxelPersist = true;
|
||||||
|
@ -75,7 +74,6 @@ VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : Assign
|
||||||
_serverTree(true) {
|
_serverTree(true) {
|
||||||
_argc = 0;
|
_argc = 0;
|
||||||
_argv = NULL;
|
_argv = NULL;
|
||||||
_dontKillOnMissingDomain = false;
|
|
||||||
|
|
||||||
_packetsPerClientPerInterval = 10;
|
_packetsPerClientPerInterval = 10;
|
||||||
_wantVoxelPersist = true;
|
_wantVoxelPersist = true;
|
||||||
|
@ -167,25 +165,6 @@ void VoxelServer::parsePayload() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelServer::setupStandAlone(const char* domain, int port) {
|
|
||||||
NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, port);
|
|
||||||
|
|
||||||
// Handle Local Domain testing with the --local command line
|
|
||||||
const char* local = "--local";
|
|
||||||
_wantLocalDomain = strcmp(domain, local) == 0;
|
|
||||||
if (_wantLocalDomain) {
|
|
||||||
qDebug("Local Domain MODE!\n");
|
|
||||||
NodeList::getInstance()->setDomainIPToLocalhost();
|
|
||||||
} else {
|
|
||||||
if (domain) {
|
|
||||||
NodeList::getInstance()->setDomainHostname(domain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're running in standalone mode, we don't want to kill ourselves when we haven't heard from a domain
|
|
||||||
_dontKillOnMissingDomain = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//int main(int argc, const char * argv[]) {
|
//int main(int argc, const char * argv[]) {
|
||||||
void VoxelServer::run() {
|
void VoxelServer::run() {
|
||||||
|
|
||||||
|
@ -372,8 +351,7 @@ void VoxelServer::run() {
|
||||||
// loop to send to nodes requesting data
|
// loop to send to nodes requesting data
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
if (!_dontKillOnMissingDomain &&
|
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||||
NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,13 +35,6 @@ public:
|
||||||
/// allows setting of run arguments
|
/// allows setting of run arguments
|
||||||
void setArguments(int argc, char** argv);
|
void setArguments(int argc, char** argv);
|
||||||
|
|
||||||
/// when VoxelServer class is used by voxel-server stand alone executable it calls this to specify the domain
|
|
||||||
/// and port it is handling. When called by assignment-client, this is not needed because assignment-client
|
|
||||||
/// handles ports and domains automatically.
|
|
||||||
/// \param const char* domain domain name, IP address, or local to specify the domain the voxel server is serving
|
|
||||||
/// \param int port port the voxel server will listen on
|
|
||||||
void setupStandAlone(const char* domain, int port);
|
|
||||||
|
|
||||||
bool wantsDebugVoxelSending() const { return _debugVoxelSending; }
|
bool wantsDebugVoxelSending() const { return _debugVoxelSending; }
|
||||||
bool wantsDebugVoxelReceiving() const { return _debugVoxelReceiving; }
|
bool wantsDebugVoxelReceiving() const { return _debugVoxelReceiving; }
|
||||||
bool wantShowAnimationDebug() const { return _shouldShowAnimationDebug; }
|
bool wantShowAnimationDebug() const { return _shouldShowAnimationDebug; }
|
||||||
|
@ -64,7 +57,6 @@ private:
|
||||||
int _argc;
|
int _argc;
|
||||||
const char** _argv;
|
const char** _argv;
|
||||||
char** _parsedArgV;
|
char** _parsedArgV;
|
||||||
bool _dontKillOnMissingDomain;
|
|
||||||
|
|
||||||
char _voxelPersistFilename[MAX_FILENAME_LENGTH];
|
char _voxelPersistFilename[MAX_FILENAME_LENGTH];
|
||||||
int _packetsPerClientPerInterval;
|
int _packetsPerClientPerInterval;
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 2.8)
|
|
||||||
|
|
||||||
set(TARGET_NAME voxel-server)
|
|
||||||
|
|
||||||
set(ROOT_DIR ..)
|
|
||||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
|
||||||
|
|
||||||
# setup for find modules
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
|
|
||||||
|
|
||||||
# set up the external glm library
|
|
||||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
|
||||||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
|
||||||
|
|
||||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
|
||||||
|
|
||||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
|
||||||
|
|
||||||
# link in the shared library
|
|
||||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
|
||||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
|
||||||
|
|
||||||
# link in the hifi voxels library
|
|
||||||
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
|
|
||||||
|
|
||||||
# link in the hifi avatars library
|
|
||||||
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
|
|
||||||
|
|
||||||
# link in the hifi voxel-server-library
|
|
||||||
link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR})
|
|
Binary file not shown.
|
@ -1,86 +0,0 @@
|
||||||
NAME
|
|
||||||
voxel-server - the High Fidelity Voxel Server
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
voxel-server [--local] [--jurisdictionFile <filename>] [--port <port>] [--voxelsPersistFilename <filename>]
|
|
||||||
[--displayVoxelStats] [--debugVoxelSending] [--debugVoxelReceiving] [--shouldShowAnimationDebug]
|
|
||||||
[--wantColorRandomizer] [--NoVoxelPersist] [--packetsPerSecond <value>]
|
|
||||||
[--AddRandomVoxels] [--AddScene] [--NoAddScene]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
voxel-server is a compact, portable, scalable, distributed sparse voxel octree server
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
|
|
||||||
--local
|
|
||||||
This will run the voxel server in "local domain mode" and will look for a domain-server running on the same IP
|
|
||||||
address as the voxel server
|
|
||||||
|
|
||||||
--jurisdictionRoot [hex string of root octcode]
|
|
||||||
Tells the server to honor jurisdiction from the specified root node and below
|
|
||||||
|
|
||||||
--jurisdictionEndNodes [<octcode>(<,octcode>...)]
|
|
||||||
Tells the server to honor jurisdiction from the root down to the octcodes included in the comma separated list
|
|
||||||
|
|
||||||
--jurisdictionFile [filename]
|
|
||||||
Tells the server to load it's jurisdiction from the specified file. When a voxel server is running with a limited
|
|
||||||
"jurisdiction" it will only server voxels from that portion of the voxel tree. The jurisdiction file is a ".ini" style
|
|
||||||
file with the following options: [General/root] specifies the octal code for the node in the tree that this server will
|
|
||||||
use as it's "root". It will only server voxels under this root. [endNodes/...] a list or group of additional octalcodes
|
|
||||||
under the root, which will not be served.
|
|
||||||
|
|
||||||
The following example jurisdiction file will server all voxels from the root and below, and exclude voxels from the
|
|
||||||
voxel of scale 0.25 and 0,0,0.
|
|
||||||
|
|
||||||
****** example jurisdiction.ini **********************************************************************
|
|
||||||
[General]
|
|
||||||
root=00
|
|
||||||
|
|
||||||
[endNodes]
|
|
||||||
endnode0=0200
|
|
||||||
|
|
||||||
******************************************************************************************************
|
|
||||||
|
|
||||||
|
|
||||||
--port [port]
|
|
||||||
Specify the port the voxel-server will listen on. You must specify different ports to enable multiple voxel servers
|
|
||||||
running on a single machine.
|
|
||||||
|
|
||||||
--voxelsPersistFilename [filename]
|
|
||||||
Specify and alternate file that the voxel server will read and write persistant voxels to. By default the voxel server
|
|
||||||
will use one of the following files:
|
|
||||||
|
|
||||||
default: /etc/highfidelity/voxel-server/resources/voxels.svo
|
|
||||||
in local mode: ./resources/voxels.svo
|
|
||||||
|
|
||||||
--displayVoxelStats
|
|
||||||
Displays additional voxel stats debugging
|
|
||||||
|
|
||||||
--debugVoxelSending
|
|
||||||
Displays additional voxel sending debugging
|
|
||||||
|
|
||||||
--debugVoxelReceiving
|
|
||||||
Displays additional voxel receiving debugging
|
|
||||||
|
|
||||||
--shouldShowAnimationDebug
|
|
||||||
Displays additional verbose animation debugging
|
|
||||||
|
|
||||||
--wantColorRandomizer
|
|
||||||
Adds color randomization to inserts into the local voxel tree
|
|
||||||
|
|
||||||
--NoVoxelPersist
|
|
||||||
Disables voxel persisting
|
|
||||||
|
|
||||||
--packetsPerSecond [value]
|
|
||||||
Specifies the packets per second that this voxel server will send to attached clients
|
|
||||||
|
|
||||||
--AddRandomVoxels
|
|
||||||
Add random voxels to the surface on startup
|
|
||||||
|
|
||||||
--AddScene
|
|
||||||
OBSOLETE: Adds an arbitrary scene with several "planet" spheres
|
|
||||||
|
|
||||||
--NoAddScene
|
|
||||||
OBSOLETE: disables adding of scene
|
|
||||||
|
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
//
|
|
||||||
// main.cpp
|
|
||||||
// Voxel Server
|
|
||||||
//
|
|
||||||
// Created by Stephen Birarda on 03/06/13.
|
|
||||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
|
||||||
#include <VoxelServer.h>
|
|
||||||
const int VOXEL_LISTEN_PORT = 40106;
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, const char * argv[]) {
|
|
||||||
|
|
||||||
// Handle Local Domain testing with the --local command line
|
|
||||||
const char* local = "--local";
|
|
||||||
bool wantLocalDomain = cmdOptionExists(argc, argv, local);
|
|
||||||
const char* domainIP = getCmdOption(argc, argv, "--domain");
|
|
||||||
|
|
||||||
int listenPort = VOXEL_LISTEN_PORT;
|
|
||||||
// Check to see if the user passed in a command line option for setting listen port
|
|
||||||
const char* PORT_PARAMETER = "--port";
|
|
||||||
const char* portParameter = getCmdOption(argc, argv, PORT_PARAMETER);
|
|
||||||
if (portParameter) {
|
|
||||||
listenPort = atoi(portParameter);
|
|
||||||
if (listenPort < 1) {
|
|
||||||
listenPort = VOXEL_LISTEN_PORT;
|
|
||||||
}
|
|
||||||
printf("portParameter=%s listenPort=%d\n", portParameter, listenPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
VoxelServer ourVoxelServer(Assignment::CreateCommand);
|
|
||||||
|
|
||||||
if (wantLocalDomain) {
|
|
||||||
ourVoxelServer.setupStandAlone(local, listenPort);
|
|
||||||
} else {
|
|
||||||
if (domainIP) {
|
|
||||||
ourVoxelServer.setupStandAlone(domainIP, listenPort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ourVoxelServer.setArguments(argc, const_cast<char**>(argv));
|
|
||||||
ourVoxelServer.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue