mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 06:44:06 +02:00
merge with upstream
This commit is contained in:
commit
1047ec7c87
29 changed files with 694 additions and 350 deletions
|
@ -10,6 +10,9 @@
|
|||
//
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QSaveFile>
|
||||
#include <QThread>
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
|
@ -44,6 +47,18 @@ void MetavoxelServer::run() {
|
|||
|
||||
_lastSend = QDateTime::currentMSecsSinceEpoch();
|
||||
_sendTimer.start(SEND_INTERVAL);
|
||||
|
||||
// initialize Bitstream before using it in multiple threads
|
||||
Bitstream::preThreadingInit();
|
||||
|
||||
// create the persister and start it in its own thread
|
||||
_persister = new MetavoxelPersister(this);
|
||||
QThread* persistenceThread = new QThread(this);
|
||||
_persister->moveToThread(persistenceThread);
|
||||
persistenceThread->start();
|
||||
|
||||
// queue up the load
|
||||
QMetaObject::invokeMethod(_persister, "load");
|
||||
}
|
||||
|
||||
void MetavoxelServer::readPendingDatagrams() {
|
||||
|
@ -67,6 +82,12 @@ void MetavoxelServer::readPendingDatagrams() {
|
|||
}
|
||||
}
|
||||
|
||||
void MetavoxelServer::aboutToFinish() {
|
||||
QMetaObject::invokeMethod(_persister, "save", Q_ARG(const MetavoxelData&, _data));
|
||||
_persister->thread()->quit();
|
||||
_persister->thread()->wait();
|
||||
}
|
||||
|
||||
void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
QMutexLocker locker(&node->getMutex());
|
||||
|
@ -193,3 +214,44 @@ void MetavoxelSession::sendPacketGroup(int alreadySent) {
|
|||
_sequencer.endPacket();
|
||||
}
|
||||
}
|
||||
|
||||
MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) :
|
||||
_server(server) {
|
||||
}
|
||||
|
||||
const char* SAVE_FILE = "metavoxels.dat";
|
||||
|
||||
void MetavoxelPersister::load() {
|
||||
QFile file(SAVE_FILE);
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
}
|
||||
MetavoxelData data;
|
||||
{
|
||||
QDebug debug = qDebug() << "Reading from" << SAVE_FILE << "...";
|
||||
file.open(QIODevice::ReadOnly);
|
||||
QDataStream inStream(&file);
|
||||
Bitstream in(inStream);
|
||||
try {
|
||||
in >> data;
|
||||
} catch (const BitstreamException& e) {
|
||||
debug << "failed, " << e.getDescription();
|
||||
return;
|
||||
}
|
||||
QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data));
|
||||
debug << "done.";
|
||||
}
|
||||
data.dumpStats();
|
||||
}
|
||||
|
||||
void MetavoxelPersister::save(const MetavoxelData& data) {
|
||||
QDebug debug = qDebug() << "Writing to" << SAVE_FILE << "...";
|
||||
QSaveFile file(SAVE_FILE);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
QDataStream outStream(&file);
|
||||
Bitstream out(outStream);
|
||||
out << data;
|
||||
out.flush();
|
||||
file.commit();
|
||||
debug << "done.";
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <Endpoint.h>
|
||||
|
||||
class MetavoxelEditMessage;
|
||||
class MetavoxelPersister;
|
||||
class MetavoxelSession;
|
||||
|
||||
/// Maintains a shared metavoxel system, accepting change requests and broadcasting updates.
|
||||
|
@ -33,11 +34,15 @@ public:
|
|||
void applyEdit(const MetavoxelEditMessage& edit);
|
||||
|
||||
const MetavoxelData& getData() const { return _data; }
|
||||
|
||||
Q_INVOKABLE void setData(const MetavoxelData& data) { _data = data; }
|
||||
|
||||
virtual void run();
|
||||
|
||||
virtual void readPendingDatagrams();
|
||||
|
||||
virtual void aboutToFinish();
|
||||
|
||||
private slots:
|
||||
|
||||
void maybeAttachSession(const SharedNodePointer& node);
|
||||
|
@ -45,6 +50,8 @@ private slots:
|
|||
|
||||
private:
|
||||
|
||||
MetavoxelPersister* _persister;
|
||||
|
||||
QTimer _sendTimer;
|
||||
qint64 _lastSend;
|
||||
|
||||
|
@ -88,4 +95,20 @@ private:
|
|||
int _reliableDeltaID;
|
||||
};
|
||||
|
||||
/// Handles persistence in a separate thread.
|
||||
class MetavoxelPersister : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
MetavoxelPersister(MetavoxelServer* server);
|
||||
|
||||
Q_INVOKABLE void load();
|
||||
Q_INVOKABLE void save(const MetavoxelData& data);
|
||||
|
||||
private:
|
||||
|
||||
MetavoxelServer* _server;
|
||||
};
|
||||
|
||||
#endif // hifi_MetavoxelServer_h
|
||||
|
|
|
@ -94,23 +94,21 @@ function playClap(volume, position) {
|
|||
Audio.playSound(claps[clip], options);
|
||||
}
|
||||
|
||||
var FASTEST_CLAP_INTERVAL = 100.0;
|
||||
var SLOWEST_CLAP_INTERVAL = 2000.0;
|
||||
var FASTEST_CLAP_INTERVAL = 150.0;
|
||||
var SLOWEST_CLAP_INTERVAL = 750.0;
|
||||
|
||||
Controller.keyPressEvent.connect(function(event) {
|
||||
if(event.text == "SHIFT") {
|
||||
if (!clickClappingNow) {
|
||||
clickClappingNow = true;
|
||||
clickStartTime = new Date();
|
||||
playClap(1.0, Camera.getPosition());
|
||||
lastClapFrame = 0;
|
||||
MyAvatar.startAnimation(clapAnimation, clapRate, 1.0, true, false);
|
||||
} else {
|
||||
// Adjust animation speed for measured clicking interval
|
||||
// start or adjust clapping speed based on the duration between clicks
|
||||
clickEndTime = new Date();
|
||||
var milliseconds = clickEndTime - clickStartTime;
|
||||
var milliseconds = Math.max(clickEndTime - clickStartTime, FASTEST_CLAP_INTERVAL);
|
||||
clickStartTime = new Date();
|
||||
if ((milliseconds < SLOWEST_CLAP_INTERVAL) && (milliseconds > FASTEST_CLAP_INTERVAL)) {
|
||||
if (milliseconds < SLOWEST_CLAP_INTERVAL) {
|
||||
clapRate = ANIMATION_FRAMES_PER_CLAP * (1000.0 / milliseconds);
|
||||
playClap(1.0, Camera.getPosition());
|
||||
MyAvatar.stopAnimation(clapAnimation);
|
||||
|
|
|
@ -112,11 +112,10 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) :
|
|||
_scopeInput(0),
|
||||
_scopeOutputLeft(0),
|
||||
_scopeOutputRight(0),
|
||||
_audioMixerAvatarStreamAudioStats(),
|
||||
_outgoingAvatarAudioSequenceNumber(0),
|
||||
_incomingMixedAudioSequenceNumberStats(INCOMING_SEQ_STATS_HISTORY_LENGTH),
|
||||
_starveCount(0),
|
||||
_consecutiveNotMixedCount(0),
|
||||
_outgoingAvatarAudioSequenceNumber(0),
|
||||
_incomingMixedAudioSequenceNumberStats(INCOMING_SEQ_STATS_HISTORY_LENGTH),
|
||||
_interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS)
|
||||
{
|
||||
// clear the array of locally injected samples
|
||||
|
|
|
@ -15,21 +15,189 @@
|
|||
#include "Util.h"
|
||||
#include "world.h"
|
||||
|
||||
const float HAIR_DAMPING = 0.99f;
|
||||
const float CONSTRAINT_RELAXATION = 10.0f;
|
||||
const float HAIR_ACCELERATION_COUPLING = 0.025f;
|
||||
const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f;
|
||||
const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f;
|
||||
const float HAIR_STIFFNESS = 0.005f;
|
||||
const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f);
|
||||
const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f);
|
||||
|
||||
Hair::Hair() {
|
||||
qDebug() << "Creating Hair";
|
||||
Hair::Hair(int strands,
|
||||
int links,
|
||||
float radius,
|
||||
float linkLength,
|
||||
float hairThickness) :
|
||||
_strands(strands),
|
||||
_links(links),
|
||||
_linkLength(linkLength),
|
||||
_hairThickness(hairThickness),
|
||||
_radius(radius),
|
||||
_acceleration(0.0f),
|
||||
_angularVelocity(0.0f),
|
||||
_gravity(0.0f)
|
||||
{
|
||||
_hairPosition = new glm::vec3[_strands * _links];
|
||||
_hairOriginalPosition = new glm::vec3[_strands * _links];
|
||||
_hairLastPosition = new glm::vec3[_strands * _links];
|
||||
_hairQuadDelta = new glm::vec3[_strands * _links];
|
||||
_hairNormals = new glm::vec3[_strands * _links];
|
||||
_hairColors = new glm::vec3[_strands * _links];
|
||||
_hairIsMoveable = new int[_strands * _links];
|
||||
_hairConstraints = new int[_strands * _links * HAIR_CONSTRAINTS]; // Hair can link to two others
|
||||
const float FACE_WIDTH = PI / 4.0f;
|
||||
glm::vec3 thisVertex;
|
||||
for (int strand = 0; strand < _strands; strand++) {
|
||||
float strandAngle = randFloat() * PI;
|
||||
float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH));
|
||||
float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI);
|
||||
glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation));
|
||||
thisStrand *= _radius;
|
||||
|
||||
for (int link = 0; link < _links; link++) {
|
||||
int vertexIndex = strand * _links + link;
|
||||
// Clear constraints
|
||||
for (int link2 = 0; link2 < HAIR_CONSTRAINTS; link2++) {
|
||||
_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link2] = -1;
|
||||
}
|
||||
if (vertexIndex % _links == 0) {
|
||||
// start of strand
|
||||
thisVertex = thisStrand;
|
||||
} else {
|
||||
thisVertex+= glm::normalize(thisStrand) * _linkLength;
|
||||
// Set constraints to vertex before and maybe vertex after in strand
|
||||
_hairConstraints[vertexIndex * HAIR_CONSTRAINTS] = vertexIndex - 1;
|
||||
if (link < (_links - 1)) {
|
||||
_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + 1] = vertexIndex + 1;
|
||||
}
|
||||
}
|
||||
_hairOriginalPosition[vertexIndex] = _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex] = thisVertex;
|
||||
|
||||
_hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.f, sin(strandAngle) * _hairThickness);
|
||||
_hairQuadDelta[vertexIndex] *= 1.f - ((float)link / _links);
|
||||
_hairNormals[vertexIndex] = glm::normalize(randVector());
|
||||
if (randFloat() < elevation / PI_OVER_TWO) {
|
||||
_hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)_links);
|
||||
} else {
|
||||
_hairColors[vertexIndex] = HAIR_COLOR2 * ((float)(link + 1) / (float)_links);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hair::simulate(float deltaTime) {
|
||||
deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f);
|
||||
glm::vec3 acceleration = _acceleration;
|
||||
if (glm::length(acceleration) > HAIR_MAX_LINEAR_ACCELERATION) {
|
||||
acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION;
|
||||
}
|
||||
|
||||
for (int strand = 0; strand < _strands; strand++) {
|
||||
for (int link = 0; link < _links; link++) {
|
||||
int vertexIndex = strand * _links + link;
|
||||
if (vertexIndex % _links == 0) {
|
||||
// Base Joint - no integration
|
||||
} else {
|
||||
//
|
||||
// Vertlet Integration
|
||||
//
|
||||
// Add velocity from last position, with damping
|
||||
glm::vec3 thisPosition = _hairPosition[vertexIndex];
|
||||
glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex];
|
||||
_hairPosition[vertexIndex] += diff * HAIR_DAMPING;
|
||||
|
||||
// Resolve collisions with sphere
|
||||
if (glm::length(_hairPosition[vertexIndex]) < _radius) {
|
||||
_hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) *
|
||||
(_radius - glm::length(_hairPosition[vertexIndex]));
|
||||
}
|
||||
|
||||
// Add gravity
|
||||
_hairPosition[vertexIndex] += _gravity * deltaTime;
|
||||
|
||||
// Add linear acceleration
|
||||
_hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime;
|
||||
|
||||
// Add stiffness to return to original position
|
||||
_hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex])
|
||||
* powf(1.f - (float)link / _links, 2.f) * HAIR_STIFFNESS;
|
||||
|
||||
// Add angular acceleration
|
||||
const float ANGULAR_VELOCITY_MIN = 0.001f;
|
||||
if (glm::length(_angularVelocity) > ANGULAR_VELOCITY_MIN) {
|
||||
glm::vec3 yawVector = _hairPosition[vertexIndex];
|
||||
yawVector.y = 0.f;
|
||||
if (glm::length(yawVector) > EPSILON) {
|
||||
float radius = glm::length(yawVector);
|
||||
yawVector = glm::normalize(yawVector);
|
||||
float angle = atan2f(yawVector.x, -yawVector.z) + PI;
|
||||
glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0));
|
||||
_hairPosition[vertexIndex] -= delta * radius * _angularVelocity.y * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime;
|
||||
}
|
||||
glm::vec3 pitchVector = _hairPosition[vertexIndex];
|
||||
pitchVector.x = 0.f;
|
||||
if (glm::length(pitchVector) > EPSILON) {
|
||||
float radius = glm::length(pitchVector);
|
||||
pitchVector = glm::normalize(pitchVector);
|
||||
float angle = atan2f(pitchVector.y, -pitchVector.z) + PI;
|
||||
glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0));
|
||||
_hairPosition[vertexIndex] -= delta * radius * _angularVelocity.x * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime;
|
||||
}
|
||||
glm::vec3 rollVector = _hairPosition[vertexIndex];
|
||||
rollVector.z = 0.f;
|
||||
if (glm::length(rollVector) > EPSILON) {
|
||||
float radius = glm::length(rollVector);
|
||||
pitchVector = glm::normalize(rollVector);
|
||||
float angle = atan2f(rollVector.x, rollVector.y) + PI;
|
||||
glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1));
|
||||
_hairPosition[vertexIndex] -= delta * radius * _angularVelocity.z * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Impose link constraints
|
||||
for (int link = 0; link < HAIR_CONSTRAINTS; link++) {
|
||||
if (_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link] > -1) {
|
||||
// If there is a constraint, try to enforce it
|
||||
glm::vec3 vectorBetween = _hairPosition[_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link]] - _hairPosition[vertexIndex];
|
||||
_hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - _linkLength) * CONSTRAINT_RELAXATION * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Store start position for next vertlet pass
|
||||
_hairLastPosition[vertexIndex] = thisPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hair::render() {
|
||||
//
|
||||
// Before calling this function, translate/rotate to the origin of the owning object
|
||||
glPushMatrix();
|
||||
glColor3f(1.0f, 1.0f, 0.0f);
|
||||
glutSolidSphere(1.0f, 15, 15);
|
||||
glPopMatrix();
|
||||
//
|
||||
glBegin(GL_QUADS);
|
||||
for (int strand = 0; strand < _strands; strand++) {
|
||||
for (int link = 0; link < _links - 1; link++) {
|
||||
int vertexIndex = strand * _links + link;
|
||||
glColor3fv(&_hairColors[vertexIndex].x);
|
||||
glNormal3fv(&_hairNormals[vertexIndex].x);
|
||||
glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y,
|
||||
_hairPosition[vertexIndex].z - _hairQuadDelta[vertexIndex].z);
|
||||
glVertex3f(_hairPosition[vertexIndex].x + _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex].y + _hairQuadDelta[vertexIndex].y,
|
||||
_hairPosition[vertexIndex].z + _hairQuadDelta[vertexIndex].z);
|
||||
|
||||
glVertex3f(_hairPosition[vertexIndex + 1].x + _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex + 1].y + _hairQuadDelta[vertexIndex].y,
|
||||
_hairPosition[vertexIndex + 1].z + _hairQuadDelta[vertexIndex].z);
|
||||
glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y,
|
||||
_hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -21,14 +21,45 @@
|
|||
#include "InterfaceConfig.h"
|
||||
#include "Util.h"
|
||||
|
||||
const int HAIR_CONSTRAINTS = 2;
|
||||
|
||||
const int DEFAULT_HAIR_STRANDS = 50;
|
||||
const int DEFAULT_HAIR_LINKS = 10;
|
||||
const float DEFAULT_HAIR_RADIUS = 0.15;
|
||||
const float DEFAULT_HAIR_LINK_LENGTH = 0.03;
|
||||
const float DEFAULT_HAIR_THICKNESS = 0.015;
|
||||
|
||||
class Hair {
|
||||
public:
|
||||
Hair();
|
||||
Hair(int strands = DEFAULT_HAIR_STRANDS,
|
||||
int links = DEFAULT_HAIR_LINKS,
|
||||
float radius = DEFAULT_HAIR_RADIUS,
|
||||
float linkLength = DEFAULT_HAIR_LINK_LENGTH,
|
||||
float hairThickness = DEFAULT_HAIR_THICKNESS);
|
||||
void simulate(float deltaTime);
|
||||
void render();
|
||||
void setAcceleration(const glm::vec3& acceleration) { _acceleration = acceleration; }
|
||||
void setAngularVelocity(const glm::vec3& angularVelocity) { _angularVelocity = angularVelocity; }
|
||||
void setGravity(const glm::vec3& gravity) { _gravity = gravity; }
|
||||
|
||||
private:
|
||||
int _strands;
|
||||
int _links;
|
||||
float _linkLength;
|
||||
float _hairThickness;
|
||||
float _radius;
|
||||
glm::vec3* _hairPosition;
|
||||
glm::vec3* _hairOriginalPosition;
|
||||
glm::vec3* _hairLastPosition;
|
||||
glm::vec3* _hairQuadDelta;
|
||||
glm::vec3* _hairNormals;
|
||||
glm::vec3* _hairColors;
|
||||
int* _hairIsMoveable;
|
||||
int* _hairConstraints;
|
||||
glm::vec3 _acceleration;
|
||||
glm::vec3 _angularVelocity;
|
||||
glm::vec3 _gravity;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -441,13 +441,11 @@ Menu::Menu() :
|
|||
QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools");
|
||||
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandDisplaySideTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandAvatarSimulateTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandAvatarUpdateTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMiscAvatarTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandIdleTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
|
||||
|
|
|
@ -357,13 +357,11 @@ namespace MenuOption {
|
|||
const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableVRMode = "Enable VR Mode";
|
||||
const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing";
|
||||
const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing";
|
||||
const QString ExpandAvatarSimulateTiming = "Expand MyAvatar simulate Timing";
|
||||
const QString ExpandDisplaySideTiming = "Expand Display Side Timing";
|
||||
const QString ExpandIdleTiming = "Expand Idle Timing";
|
||||
const QString ExpandPaintGLTiming = "Expand PaintGL Timing";
|
||||
const QString ExpandUpdateTiming = "Expand Update Timing";
|
||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
||||
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
||||
const QString ExpandPaintGLTiming = "Expand /paintGL";
|
||||
const QString ExpandUpdateTiming = "Expand /update";
|
||||
const QString Faceplus = "Faceplus";
|
||||
const QString Faceshift = "Faceshift";
|
||||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
|
|
|
@ -48,8 +48,10 @@ void MetavoxelSystem::init() {
|
|||
}
|
||||
|
||||
MetavoxelLOD MetavoxelSystem::getLOD() const {
|
||||
const float FIXED_LOD_THRESHOLD = 0.01f;
|
||||
return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD);
|
||||
// the LOD threshold is temporarily tied to the avatar LOD parameter
|
||||
const float BASE_LOD_THRESHOLD = 0.01f;
|
||||
return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(),
|
||||
BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier());
|
||||
}
|
||||
|
||||
void MetavoxelSystem::simulate(float deltaTime) {
|
||||
|
|
|
@ -82,8 +82,7 @@ void Avatar::init() {
|
|||
_skeletonModel.init();
|
||||
_initialized = true;
|
||||
_shouldRenderBillboard = (getLODDistance() >= BILLBOARD_LOD_DISTANCE);
|
||||
initializeHair();
|
||||
|
||||
|
||||
for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) {
|
||||
_localLightColors[i] = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
_localLightDirections[i] = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
|
@ -168,13 +167,11 @@ void Avatar::simulate(float deltaTime) {
|
|||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
|
||||
PerformanceTimer perfTimer("hair");
|
||||
simulateHair(deltaTime);
|
||||
_hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.simulate(deltaTime);
|
||||
}
|
||||
|
||||
foreach (Hair* hair, _hairs) {
|
||||
hair->simulate(deltaTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// update position by velocity, and subtract the change added earlier for gravity
|
||||
|
@ -426,233 +423,17 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) {
|
|||
getHand()->render(false, modelRenderMode);
|
||||
}
|
||||
getHead()->render(1.0f, modelRenderMode);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
|
||||
renderHair();
|
||||
foreach (Hair* hair, _hairs) {
|
||||
hair->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Constants for the Hair Simulation
|
||||
//
|
||||
|
||||
const float HAIR_LENGTH = 0.2f;
|
||||
const float HAIR_LINK_LENGTH = HAIR_LENGTH / HAIR_LINKS;
|
||||
const float HAIR_DAMPING = 0.99f;
|
||||
const float HEAD_RADIUS = 0.21f;
|
||||
const float CONSTRAINT_RELAXATION = 10.0f;
|
||||
const glm::vec3 HAIR_GRAVITY(0.0f, -0.007f, 0.0f);
|
||||
const float HAIR_ACCELERATION_COUPLING = 0.025f;
|
||||
const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f;
|
||||
const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f;
|
||||
const float HAIR_THICKNESS = 0.015f;
|
||||
const float HAIR_STIFFNESS = 0.0000f;
|
||||
const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f);
|
||||
const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f);
|
||||
const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.0f);
|
||||
const float MAX_WIND_STRENGTH = 0.02f;
|
||||
const float FINGER_LENGTH = 0.25f;
|
||||
const float FINGER_RADIUS = 0.10f;
|
||||
|
||||
void Avatar::renderHair() {
|
||||
//
|
||||
// Render the avatar's moveable hair
|
||||
//
|
||||
|
||||
glm::vec3 headPosition = getHead()->getPosition();
|
||||
glPushMatrix();
|
||||
glTranslatef(headPosition.x, headPosition.y, headPosition.z);
|
||||
const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame();
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
for (int strand = 0; strand < HAIR_STRANDS; strand++) {
|
||||
for (int link = 0; link < HAIR_LINKS - 1; link++) {
|
||||
int vertexIndex = strand * HAIR_LINKS + link;
|
||||
glColor3fv(&_hairColors[vertexIndex].x);
|
||||
glNormal3fv(&_hairNormals[vertexIndex].x);
|
||||
glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y,
|
||||
_hairPosition[vertexIndex].z - _hairQuadDelta[vertexIndex].z);
|
||||
glVertex3f(_hairPosition[vertexIndex].x + _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex].y + _hairQuadDelta[vertexIndex].y,
|
||||
_hairPosition[vertexIndex].z + _hairQuadDelta[vertexIndex].z);
|
||||
|
||||
glVertex3f(_hairPosition[vertexIndex + 1].x + _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex + 1].y + _hairQuadDelta[vertexIndex].y,
|
||||
_hairPosition[vertexIndex + 1].z + _hairQuadDelta[vertexIndex].z);
|
||||
glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x,
|
||||
_hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y,
|
||||
_hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
void Avatar::simulateHair(float deltaTime) {
|
||||
|
||||
deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f);
|
||||
glm::vec3 acceleration = getAcceleration();
|
||||
if (glm::length(acceleration) > HAIR_MAX_LINEAR_ACCELERATION) {
|
||||
acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION;
|
||||
}
|
||||
const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame();
|
||||
acceleration = acceleration * rotation;
|
||||
glm::vec3 angularVelocity = getAngularVelocity() + getHead()->getAngularVelocity();
|
||||
|
||||
// Get hand positions to allow touching hair
|
||||
glm::vec3 leftHandPosition, rightHandPosition;
|
||||
getSkeletonModel().getLeftHandPosition(leftHandPosition);
|
||||
getSkeletonModel().getRightHandPosition(rightHandPosition);
|
||||
leftHandPosition -= getHead()->getPosition();
|
||||
rightHandPosition -= getHead()->getPosition();
|
||||
glm::quat leftRotation, rightRotation;
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
|
||||
leftHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.0f) * glm::inverse(leftRotation);
|
||||
rightHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.0f) * glm::inverse(rightRotation);
|
||||
leftHandPosition = leftHandPosition * rotation;
|
||||
rightHandPosition = rightHandPosition * rotation;
|
||||
|
||||
float windIntensity = randFloat() * MAX_WIND_STRENGTH;
|
||||
|
||||
for (int strand = 0; strand < HAIR_STRANDS; strand++) {
|
||||
for (int link = 0; link < HAIR_LINKS; link++) {
|
||||
int vertexIndex = strand * HAIR_LINKS + link;
|
||||
if (vertexIndex % HAIR_LINKS == 0) {
|
||||
// Base Joint - no integration
|
||||
} else {
|
||||
//
|
||||
// Vertlet Integration
|
||||
//
|
||||
// Add velocity from last position, with damping
|
||||
glm::vec3 thisPosition = _hairPosition[vertexIndex];
|
||||
glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex];
|
||||
_hairPosition[vertexIndex] += diff * HAIR_DAMPING;
|
||||
// Resolve collision with head sphere
|
||||
if (glm::length(_hairPosition[vertexIndex]) < HEAD_RADIUS) {
|
||||
_hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) *
|
||||
(HEAD_RADIUS - glm::length(_hairPosition[vertexIndex]));
|
||||
}
|
||||
// Resolve collision with hands
|
||||
if (glm::length(_hairPosition[vertexIndex] - leftHandPosition) < FINGER_RADIUS) {
|
||||
_hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - leftHandPosition) *
|
||||
(FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - leftHandPosition));
|
||||
}
|
||||
if (glm::length(_hairPosition[vertexIndex] - rightHandPosition) < FINGER_RADIUS) {
|
||||
_hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - rightHandPosition) *
|
||||
(FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - rightHandPosition));
|
||||
}
|
||||
|
||||
|
||||
// Add a little gravity
|
||||
_hairPosition[vertexIndex] += HAIR_GRAVITY * rotation * deltaTime;
|
||||
|
||||
// Add linear acceleration of the avatar body
|
||||
_hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime;
|
||||
|
||||
// Add stiffness (like hair care products do)
|
||||
_hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex])
|
||||
* powf(1.f - link / HAIR_LINKS, 2.f) * HAIR_STIFFNESS;
|
||||
|
||||
// Add some wind
|
||||
glm::vec3 wind = WIND_DIRECTION * windIntensity;
|
||||
_hairPosition[vertexIndex] += wind * deltaTime;
|
||||
|
||||
const float ANGULAR_VELOCITY_MIN = 0.001f;
|
||||
// Add angular acceleration of the avatar body
|
||||
if (glm::length(angularVelocity) > ANGULAR_VELOCITY_MIN) {
|
||||
glm::vec3 yawVector = _hairPosition[vertexIndex];
|
||||
yawVector.y = 0.f;
|
||||
if (glm::length(yawVector) > EPSILON) {
|
||||
float radius = glm::length(yawVector);
|
||||
yawVector = glm::normalize(yawVector);
|
||||
float angle = atan2f(yawVector.x, -yawVector.z) + PI;
|
||||
glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0));
|
||||
_hairPosition[vertexIndex] -= delta * radius * angularVelocity.y * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime;
|
||||
}
|
||||
glm::vec3 pitchVector = _hairPosition[vertexIndex];
|
||||
pitchVector.x = 0.f;
|
||||
if (glm::length(pitchVector) > EPSILON) {
|
||||
float radius = glm::length(pitchVector);
|
||||
pitchVector = glm::normalize(pitchVector);
|
||||
float angle = atan2f(pitchVector.y, -pitchVector.z) + PI;
|
||||
glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0));
|
||||
_hairPosition[vertexIndex] -= delta * radius * angularVelocity.x * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime;
|
||||
}
|
||||
glm::vec3 rollVector = _hairPosition[vertexIndex];
|
||||
rollVector.z = 0.f;
|
||||
if (glm::length(rollVector) > EPSILON) {
|
||||
float radius = glm::length(rollVector);
|
||||
pitchVector = glm::normalize(rollVector);
|
||||
float angle = atan2f(rollVector.x, rollVector.y) + PI;
|
||||
glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1));
|
||||
_hairPosition[vertexIndex] -= delta * radius * angularVelocity.z * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate length constraints to other links
|
||||
for (int link = 0; link < HAIR_MAX_CONSTRAINTS; link++) {
|
||||
if (_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link] > -1) {
|
||||
// If there is a constraint, try to enforce it
|
||||
glm::vec3 vectorBetween = _hairPosition[_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link]] - _hairPosition[vertexIndex];
|
||||
_hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - HAIR_LINK_LENGTH) * CONSTRAINT_RELAXATION * deltaTime;
|
||||
}
|
||||
}
|
||||
// Store start position for next vertlet pass
|
||||
_hairLastPosition[vertexIndex] = thisPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::initializeHair() {
|
||||
const float FACE_WIDTH = PI / 4.0f;
|
||||
glm::vec3 thisVertex;
|
||||
for (int strand = 0; strand < HAIR_STRANDS; strand++) {
|
||||
float strandAngle = randFloat() * PI;
|
||||
float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH));
|
||||
float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI);
|
||||
glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation));
|
||||
thisStrand *= HEAD_RADIUS;
|
||||
|
||||
for (int link = 0; link < HAIR_LINKS; link++) {
|
||||
int vertexIndex = strand * HAIR_LINKS + link;
|
||||
// Clear constraints
|
||||
for (int link2 = 0; link2 < HAIR_MAX_CONSTRAINTS; link2++) {
|
||||
_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link2] = -1;
|
||||
}
|
||||
if (vertexIndex % HAIR_LINKS == 0) {
|
||||
// start of strand
|
||||
thisVertex = thisStrand;
|
||||
} else {
|
||||
thisVertex+= glm::normalize(thisStrand) * HAIR_LINK_LENGTH;
|
||||
// Set constraints to vertex before and maybe vertex after in strand
|
||||
_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS] = vertexIndex - 1;
|
||||
if (link < (HAIR_LINKS - 1)) {
|
||||
_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + 1] = vertexIndex + 1;
|
||||
}
|
||||
}
|
||||
_hairPosition[vertexIndex] = thisVertex;
|
||||
_hairLastPosition[vertexIndex] = _hairPosition[vertexIndex];
|
||||
_hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex];
|
||||
|
||||
_hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * HAIR_THICKNESS, 0.f, sin(strandAngle) * HAIR_THICKNESS);
|
||||
_hairQuadDelta[vertexIndex] *= 1.f - (link / HAIR_LINKS);
|
||||
_hairNormals[vertexIndex] = glm::normalize(randVector());
|
||||
if (randFloat() < elevation / PI_OVER_TWO) {
|
||||
_hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)HAIR_LINKS);
|
||||
} else {
|
||||
_hairColors[vertexIndex] = HAIR_COLOR2 * ((float)(link + 1) / (float)HAIR_LINKS);
|
||||
}
|
||||
|
||||
}
|
||||
// Render Hair
|
||||
glPushMatrix();
|
||||
glm::vec3 headPosition = getHead()->getPosition();
|
||||
glTranslatef(headPosition.x, headPosition.y, headPosition.z);
|
||||
const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame();
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
_hair.render();
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ signals:
|
|||
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
|
||||
|
||||
protected:
|
||||
QVector<Hair*> _hairs;
|
||||
Hair _hair;
|
||||
SkeletonModel _skeletonModel;
|
||||
QVector<Model*> _attachmentModels;
|
||||
float _bodyYawDelta;
|
||||
|
@ -214,18 +214,6 @@ protected:
|
|||
virtual void renderAttachments(RenderMode renderMode);
|
||||
|
||||
virtual void updateJointMappings();
|
||||
|
||||
glm::vec3 _hairPosition[HAIR_STRANDS * HAIR_LINKS];
|
||||
glm::vec3 _hairOriginalPosition[HAIR_STRANDS * HAIR_LINKS];
|
||||
glm::vec3 _hairLastPosition[HAIR_STRANDS * HAIR_LINKS];
|
||||
glm::vec3 _hairQuadDelta[HAIR_STRANDS * HAIR_LINKS];
|
||||
glm::vec3 _hairNormals[HAIR_STRANDS * HAIR_LINKS];
|
||||
glm::vec3 _hairColors[HAIR_STRANDS * HAIR_LINKS];
|
||||
int _hairIsMoveable[HAIR_STRANDS * HAIR_LINKS];
|
||||
int _hairConstraints[HAIR_STRANDS * HAIR_LINKS * 2]; // Hair can link to two others
|
||||
void renderHair();
|
||||
void simulateHair(float deltaTime);
|
||||
void initializeHair();
|
||||
|
||||
private:
|
||||
|
||||
|
@ -238,6 +226,7 @@ private:
|
|||
|
||||
float getBillboardSize() const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_Avatar_h
|
||||
|
|
|
@ -185,10 +185,10 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
{
|
||||
PerformanceTimer perfTimer("hair");
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
|
||||
simulateHair(deltaTime);
|
||||
foreach (Hair* hair, _hairs) {
|
||||
hair->simulate(deltaTime);
|
||||
}
|
||||
_hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame());
|
||||
_hair.simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -884,11 +884,17 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) {
|
|||
const glm::vec3 cameraPos = camera->getPosition() + (camera->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) * camera->getDistance();
|
||||
if (shouldRenderHead(cameraPos, renderMode)) {
|
||||
getHead()->render(1.0f, modelRenderMode);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) {
|
||||
renderHair();
|
||||
foreach (Hair* hair, _hairs) {
|
||||
hair->render();
|
||||
}
|
||||
// Render Hair
|
||||
glPushMatrix();
|
||||
glm::vec3 headPosition = getHead()->getPosition();
|
||||
glTranslatef(headPosition.x, headPosition.y, headPosition.z);
|
||||
const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame();
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
_hair.render();
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
getHand()->render(true, modelRenderMode);
|
||||
|
|
|
@ -162,36 +162,25 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh
|
|||
}
|
||||
|
||||
bool Stats::includeTimingRecord(const QString& name) {
|
||||
bool included = false;
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
|
||||
|
||||
if (name == "idle/update") {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming) ||
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming);
|
||||
} else if (name == "idle/updateGL") {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming);
|
||||
} else if (name.startsWith("idle/update")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming);
|
||||
} else if (name.startsWith("idle/")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming);
|
||||
} else if (name.startsWith("MyAvatar::simulate")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandAvatarSimulateTiming);
|
||||
} else if (name.startsWith("MyAvatar::update/") || name.startsWith("updateMyAvatar")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandAvatarUpdateTiming);
|
||||
} else if (name.startsWith("MyAvatar::")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandMiscAvatarTiming);
|
||||
} else if (name == "paintGL/displaySide") {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandDisplaySideTiming) ||
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||
} else if (name.startsWith("paintGL/displaySide/")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandDisplaySideTiming);
|
||||
} else if (name.startsWith("paintGL/")) {
|
||||
included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||
} else {
|
||||
included = true; // include everything else
|
||||
if (name.startsWith("/idle/update/")) {
|
||||
if (name.startsWith("/idle/update/myAvatar/")) {
|
||||
if (name.startsWith("/idle/update/myAvatar/simulate/")) {
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandMyAvatarSimulateTiming);
|
||||
}
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandMyAvatarTiming);
|
||||
} else if (name.startsWith("/idle/update/otherAvatars/")) {
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandOtherAvatarTiming);
|
||||
}
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming);
|
||||
} else if (name.startsWith("/idle/updateGL/paintGL/")) {
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||
} else if (name.startsWith("/paintGL/")) {
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return included;
|
||||
return false;
|
||||
}
|
||||
|
||||
// display expanded or contracted stats
|
||||
|
@ -441,7 +430,7 @@ void Stats::display(
|
|||
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
|
||||
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||
|
||||
lines = _expanded ? 5 : 3;
|
||||
lines = _expanded ? 8 : 3;
|
||||
|
||||
drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, lines * STATS_PELS_PER_LINE + 10);
|
||||
horizontalOffset += 5;
|
||||
|
@ -483,6 +472,41 @@ void Stats::display(
|
|||
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color);
|
||||
|
||||
int internal = 0, leaves = 0;
|
||||
int sendProgress = 0, sendTotal = 0;
|
||||
int receiveProgress = 0, receiveTotal = 0;
|
||||
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
|
||||
if (node->getType() == NodeType::MetavoxelServer) {
|
||||
QMutexLocker locker(&node->getMutex());
|
||||
MetavoxelClient* client = static_cast<MetavoxelSystemClient*>(node->getLinkedData());
|
||||
if (client) {
|
||||
client->getData().countNodes(internal, leaves, Application::getInstance()->getMetavoxels()->getLOD());
|
||||
client->getSequencer().addReliableChannelStats(sendProgress, sendTotal, receiveProgress, receiveTotal);
|
||||
}
|
||||
}
|
||||
}
|
||||
stringstream nodes;
|
||||
nodes << "Metavoxels: " << (internal + leaves);
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodes.str().c_str(), color);
|
||||
|
||||
stringstream nodeTypes;
|
||||
nodeTypes << "Internal: " << internal << " Leaves: " << leaves;
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodeTypes.str().c_str(), color);
|
||||
|
||||
if (sendTotal > 0 || receiveTotal > 0) {
|
||||
stringstream reliableStats;
|
||||
if (sendTotal > 0) {
|
||||
reliableStats << "Upload: " << (sendProgress * 100 / sendTotal) << "% ";
|
||||
}
|
||||
if (receiveTotal > 0) {
|
||||
reliableStats << "Download: " << (receiveProgress * 100 / receiveTotal) << "%";
|
||||
}
|
||||
verticalOffset += STATS_PELS_PER_LINE;
|
||||
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableStats.str().c_str(), color);
|
||||
}
|
||||
}
|
||||
|
||||
verticalOffset = 0;
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QReadLocker>
|
||||
#include <QScriptEngine>
|
||||
#include <QWriteLocker>
|
||||
|
||||
#include "AttributeRegistry.h"
|
||||
#include "MetavoxelData.h"
|
||||
|
@ -69,6 +71,7 @@ AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute
|
|||
if (!attribute) {
|
||||
return attribute;
|
||||
}
|
||||
QWriteLocker locker(&_attributesLock);
|
||||
AttributePointer& pointer = _attributes[attribute->getName()];
|
||||
if (!pointer) {
|
||||
pointer = attribute;
|
||||
|
@ -77,9 +80,15 @@ AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute
|
|||
}
|
||||
|
||||
void AttributeRegistry::deregisterAttribute(const QString& name) {
|
||||
QWriteLocker locker(&_attributesLock);
|
||||
_attributes.remove(name);
|
||||
}
|
||||
|
||||
AttributePointer AttributeRegistry::getAttribute(const QString& name) {
|
||||
QReadLocker locker(&_attributesLock);
|
||||
return _attributes.value(name);
|
||||
}
|
||||
|
||||
QScriptValue AttributeRegistry::getAttribute(QScriptContext* context, QScriptEngine* engine) {
|
||||
return engine->newQObject(getInstance()->getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership,
|
||||
QScriptEngine::PreferExistingWrapperObject);
|
||||
|
@ -559,6 +568,10 @@ void SpannerSetAttribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStream
|
|||
}
|
||||
data.insert(state.attribute, object);
|
||||
}
|
||||
// even if the root is empty, it should still exist
|
||||
if (!data.getRoot(state.attribute)) {
|
||||
data.createRoot(state.attribute);
|
||||
}
|
||||
}
|
||||
|
||||
void SpannerSetAttribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) {
|
||||
|
@ -577,6 +590,10 @@ void SpannerSetAttribute::readMetavoxelDelta(MetavoxelData& data,
|
|||
}
|
||||
data.toggle(state.attribute, object);
|
||||
}
|
||||
// even if the root is empty, it should still exist
|
||||
if (!data.getRoot(state.attribute)) {
|
||||
data.createRoot(state.attribute);
|
||||
}
|
||||
}
|
||||
|
||||
void SpannerSetAttribute::writeMetavoxelDelta(const MetavoxelNode& root,
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QReadWriteLock>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
@ -61,11 +62,14 @@ public:
|
|||
void deregisterAttribute(const QString& name);
|
||||
|
||||
/// Retrieves an attribute by name.
|
||||
AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); }
|
||||
AttributePointer getAttribute(const QString& name);
|
||||
|
||||
/// Returns a reference to the attribute hash.
|
||||
const QHash<QString, AttributePointer>& getAttributes() const { return _attributes; }
|
||||
|
||||
/// Returns a reference to the attributes lock.
|
||||
QReadWriteLock& getAttributesLock() { return _attributesLock; }
|
||||
|
||||
/// Returns a reference to the standard SharedObjectPointer "guide" attribute.
|
||||
const AttributePointer& getGuideAttribute() const { return _guideAttribute; }
|
||||
|
||||
|
@ -92,6 +96,8 @@ private:
|
|||
static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
QHash<QString, AttributePointer> _attributes;
|
||||
QReadWriteLock _attributesLock;
|
||||
|
||||
AttributePointer _guideAttribute;
|
||||
AttributePointer _spannersAttribute;
|
||||
AttributePointer _colorAttribute;
|
||||
|
|
|
@ -87,6 +87,12 @@ IDStreamer& IDStreamer::operator>>(int& value) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
void Bitstream::preThreadingInit() {
|
||||
getObjectStreamers();
|
||||
getEnumStreamers();
|
||||
getEnumStreamersByName();
|
||||
}
|
||||
|
||||
int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) {
|
||||
getMetaObjects().insert(className, metaObject);
|
||||
|
||||
|
|
|
@ -290,6 +290,11 @@ public:
|
|||
QHash<int, SharedObjectPointer> sharedObjectValues;
|
||||
};
|
||||
|
||||
/// Performs all of the various lazily initializations (of object streamers, etc.) If multiple threads need to use
|
||||
/// Bitstream instances, call this beforehand to prevent errors from occurring when multiple threads attempt lazy
|
||||
/// initialization simultaneously.
|
||||
static void preThreadingInit();
|
||||
|
||||
/// Registers a metaobject under its name so that instances of it can be streamed. Consider using the REGISTER_META_OBJECT
|
||||
/// at the top level of the source file associated with the class rather than calling this function directly.
|
||||
/// \return zero; the function only returns a value so that it can be used in static initialization
|
||||
|
|
|
@ -79,6 +79,24 @@ ReliableChannel* DatagramSequencer::getReliableInputChannel(int index) {
|
|||
return channel;
|
||||
}
|
||||
|
||||
void DatagramSequencer::addReliableChannelStats(int& sendProgress, int& sendTotal,
|
||||
int& receiveProgress, int& receiveTotal) const {
|
||||
foreach (ReliableChannel* channel, _reliableOutputChannels) {
|
||||
int sent, total;
|
||||
if (channel->getMessageSendProgress(sent, total)) {
|
||||
sendProgress += sent;
|
||||
sendTotal += total;
|
||||
}
|
||||
}
|
||||
foreach (ReliableChannel* channel, _reliableInputChannels) {
|
||||
int received, total;
|
||||
if (channel->getMessageReceiveProgress(received, total)) {
|
||||
receiveProgress += received;
|
||||
receiveTotal += total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DatagramSequencer::notePacketGroup(int desiredPackets) {
|
||||
// figure out how much data we have enqueued and increase the number of packets desired
|
||||
int totalAvailable = 0;
|
||||
|
@ -684,6 +702,8 @@ void ReliableChannel::endMessage() {
|
|||
|
||||
quint32 length = _buffer.pos() - _messageLengthPlaceholder;
|
||||
_buffer.writeBytes(_messageLengthPlaceholder, sizeof(quint32), (const char*)&length);
|
||||
_messageReceivedOffset = getBytesWritten();
|
||||
_messageSize = length;
|
||||
}
|
||||
|
||||
void ReliableChannel::sendMessage(const QVariant& message) {
|
||||
|
@ -692,6 +712,26 @@ void ReliableChannel::sendMessage(const QVariant& message) {
|
|||
endMessage();
|
||||
}
|
||||
|
||||
bool ReliableChannel::getMessageSendProgress(int& sent, int& total) const {
|
||||
if (!_messagesEnabled || _offset >= _messageReceivedOffset) {
|
||||
return false;
|
||||
}
|
||||
sent = qMax(0, _messageSize - (_messageReceivedOffset - _offset));
|
||||
total = _messageSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReliableChannel::getMessageReceiveProgress(int& received, int& total) const {
|
||||
if (!_messagesEnabled || _buffer.bytesAvailable() < (int)sizeof(quint32)) {
|
||||
return false;
|
||||
}
|
||||
quint32 length;
|
||||
_buffer.readBytes(_buffer.pos(), sizeof(quint32), (char*)&length);
|
||||
total = length;
|
||||
received = _buffer.bytesAvailable();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReliableChannel::sendClearSharedObjectMessage(int id) {
|
||||
ClearSharedObjectMessage message = { id };
|
||||
sendMessage(QVariant::fromValue(message));
|
||||
|
@ -717,7 +757,8 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o
|
|||
_offset(0),
|
||||
_writePosition(0),
|
||||
_writePositionResetPacketNumber(0),
|
||||
_messagesEnabled(true) {
|
||||
_messagesEnabled(true),
|
||||
_messageReceivedOffset(0) {
|
||||
|
||||
_buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly);
|
||||
_dataStream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
|
|
@ -108,6 +108,9 @@ public:
|
|||
/// Returns the intput channel at the specified index, creating it if necessary.
|
||||
ReliableChannel* getReliableInputChannel(int index = 0);
|
||||
|
||||
/// Adds stats for all reliable channels to the referenced variables.
|
||||
void addReliableChannelStats(int& sendProgress, int& sendTotal, int& receiveProgress, int& receiveTotal) const;
|
||||
|
||||
/// Notes that we're sending a group of packets.
|
||||
/// \param desiredPackets the number of packets we'd like to write in the group
|
||||
/// \return the number of packets to write in the group
|
||||
|
@ -376,6 +379,14 @@ public:
|
|||
/// writes the message to the bitstream, then calls endMessage).
|
||||
void sendMessage(const QVariant& message);
|
||||
|
||||
/// Determines the number of bytes uploaded towards the currently pending message.
|
||||
/// \return true if there is a message pending, in which case the sent and total arguments will be set
|
||||
bool getMessageSendProgress(int& sent, int& total) const;
|
||||
|
||||
/// Determines the number of bytes downloaded towards the currently pending message.
|
||||
/// \return true if there is a message pending, in which case the received and total arguments will be set
|
||||
bool getMessageReceiveProgress(int& received, int& total) const;
|
||||
|
||||
signals:
|
||||
|
||||
/// Fired when a framed message has been received on this channel.
|
||||
|
@ -416,6 +427,8 @@ private:
|
|||
SpanList _acknowledged;
|
||||
bool _messagesEnabled;
|
||||
int _messageLengthPlaceholder;
|
||||
int _messageReceivedOffset;
|
||||
int _messageSize;
|
||||
};
|
||||
|
||||
#endif // hifi_DatagramSequencer_h
|
||||
|
|
|
@ -32,6 +32,8 @@ public:
|
|||
PacketRecord* baselineReceiveRecord = NULL);
|
||||
virtual ~Endpoint();
|
||||
|
||||
const DatagramSequencer& getSequencer() const { return _sequencer; }
|
||||
|
||||
virtual void update();
|
||||
|
||||
virtual int parseData(const QByteArray& packet);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebugStateSaver>
|
||||
#include <QScriptEngine>
|
||||
#include <QtDebug>
|
||||
|
||||
|
@ -627,6 +628,33 @@ bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& l
|
|||
return true;
|
||||
}
|
||||
|
||||
void MetavoxelData::countNodes(int& internal, int& leaves, const MetavoxelLOD& lod) const {
|
||||
glm::vec3 minimum = getMinimum();
|
||||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
|
||||
it.value()->countNodes(it.key(), minimum, _size, lod, internal, leaves);
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelData::dumpStats(QDebug debug) const {
|
||||
QDebugStateSaver saver(debug);
|
||||
debug.nospace() << "[size=" << _size << ", roots=[";
|
||||
int totalInternal = 0, totalLeaves = 0;
|
||||
glm::vec3 minimum = getMinimum();
|
||||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
|
||||
if (it != _roots.constBegin()) {
|
||||
debug << ", ";
|
||||
}
|
||||
debug << it.key()->getName() << " (" << it.key()->metaObject()->className() << "): ";
|
||||
int internal = 0, leaves = 0;
|
||||
it.value()->countNodes(it.key(), minimum, _size, MetavoxelLOD(), internal, leaves);
|
||||
debug << internal << " internal, " << leaves << " leaves, " << (internal + leaves) << " total";
|
||||
totalInternal += internal;
|
||||
totalLeaves += leaves;
|
||||
}
|
||||
debug << "], totalInternal=" << totalInternal << ", totalLeaves=" << totalLeaves <<
|
||||
", grandTotal=" << (totalInternal + totalLeaves) << "]";
|
||||
}
|
||||
|
||||
bool MetavoxelData::operator==(const MetavoxelData& other) const {
|
||||
return _size == other._size && _roots == other._roots;
|
||||
}
|
||||
|
@ -1056,8 +1084,20 @@ void MetavoxelNode::getSpanners(const AttributePointer& attribute, const glm::ve
|
|||
}
|
||||
float nextSize = size * 0.5f;
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
glm::vec3 nextMinimum = getNextMinimum(minimum, nextSize, i);
|
||||
_children[i]->getSpanners(attribute, nextMinimum, nextSize, lod, results);
|
||||
_children[i]->getSpanners(attribute, getNextMinimum(minimum, nextSize, i), nextSize, lod, results);
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelNode::countNodes(const AttributePointer& attribute, const glm::vec3& minimum,
|
||||
float size, const MetavoxelLOD& lod, int& internal, int& leaves) const {
|
||||
if (isLeaf() || !lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) {
|
||||
leaves++;
|
||||
return;
|
||||
}
|
||||
internal++;
|
||||
float nextSize = size * 0.5f;
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
_children[i]->countNodes(attribute, getNextMinimum(minimum, nextSize, i), nextSize, lod, internal, leaves);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,6 +125,11 @@ public:
|
|||
/// shallow comparison).
|
||||
bool deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod = MetavoxelLOD()) const;
|
||||
|
||||
/// Counts the nodes in the data.
|
||||
void countNodes(int& internalNodes, int& leaves, const MetavoxelLOD& lod = MetavoxelLOD()) const;
|
||||
|
||||
void dumpStats(QDebug debug = QDebug(QtDebugMsg)) const;
|
||||
|
||||
bool operator==(const MetavoxelData& other) const;
|
||||
bool operator!=(const MetavoxelData& other) const;
|
||||
|
||||
|
@ -221,6 +226,9 @@ public:
|
|||
void getSpanners(const AttributePointer& attribute, const glm::vec3& minimum,
|
||||
float size, const MetavoxelLOD& lod, SharedObjectSet& results) const;
|
||||
|
||||
void countNodes(const AttributePointer& attribute, const glm::vec3& minimum,
|
||||
float size, const MetavoxelLOD& lod, int& internalNodes, int& leaves) const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(MetavoxelNode)
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "MetavoxelUtil.h"
|
||||
#include "ScriptCache.h"
|
||||
#include "StreamUtils.h"
|
||||
|
||||
static int scriptHashType = qRegisterMetaType<ScriptHash>();
|
||||
static int parameterizedURLType = qRegisterMetaType<ParameterizedURL>();
|
||||
|
@ -310,9 +311,8 @@ Box operator*(const glm::mat4& matrix, const Box& box) {
|
|||
return newBox;
|
||||
}
|
||||
|
||||
QDebug& operator<<(QDebug& out, const Box& box) {
|
||||
return out << '(' << box.minimum.x << box.minimum.y << box.minimum.z << ") (" <<
|
||||
box.maximum.x << box.maximum.y << box.maximum.z << ')';
|
||||
QDebug& operator<<(QDebug& dbg, const Box& box) {
|
||||
return dbg.nospace() << "{type='Box', minimum=" << box.minimum << ", maximum=" << box.maximum << "}";
|
||||
}
|
||||
|
||||
QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) {
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
STREAM glm::vec3 minimum;
|
||||
STREAM glm::vec3 maximum;
|
||||
|
||||
Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3());
|
||||
explicit Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3());
|
||||
|
||||
bool contains(const glm::vec3& point) const;
|
||||
|
||||
|
|
|
@ -357,15 +357,23 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
processedBytes = 0;
|
||||
|
||||
// the first part of the data is our octcode...
|
||||
int octets = numberOfThreeBitSectionsInCode(data);
|
||||
int octets = numberOfThreeBitSectionsInCode(data, length);
|
||||
int lengthOfOctcode = bytesRequiredForCodeLength(octets);
|
||||
|
||||
// we don't actually do anything with this octcode...
|
||||
dataAt += lengthOfOctcode;
|
||||
processedBytes += lengthOfOctcode;
|
||||
|
||||
|
||||
// id
|
||||
uint32_t editID;
|
||||
|
||||
// check to make sure we have enough content to keep reading...
|
||||
if (length - processedBytes - sizeof(editID) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
|
||||
memcpy(&editID, dataAt, sizeof(editID));
|
||||
dataAt += sizeof(editID);
|
||||
processedBytes += sizeof(editID);
|
||||
|
@ -377,6 +385,14 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
// If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that
|
||||
// we want to send back to the creator as an map to the actual id
|
||||
uint32_t creatorTokenID;
|
||||
|
||||
// check to make sure we have enough content to keep reading...
|
||||
if (length - processedBytes - sizeof(creatorTokenID) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
|
||||
memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID));
|
||||
dataAt += sizeof(creatorTokenID);
|
||||
processedBytes += sizeof(creatorTokenID);
|
||||
|
@ -409,6 +425,12 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
}
|
||||
|
||||
// lastEdited
|
||||
// check to make sure we have enough content to keep reading...
|
||||
if (length - processedBytes - sizeof(newParticle._lastEdited) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited));
|
||||
dataAt += sizeof(newParticle._lastEdited);
|
||||
processedBytes += sizeof(newParticle._lastEdited);
|
||||
|
@ -417,6 +439,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
// properties included bits
|
||||
uint16_t packetContainsBits = 0;
|
||||
if (!isNewParticle) {
|
||||
if (length - processedBytes - sizeof(packetContainsBits) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&packetContainsBits, dataAt, sizeof(packetContainsBits));
|
||||
dataAt += sizeof(packetContainsBits);
|
||||
processedBytes += sizeof(packetContainsBits);
|
||||
|
@ -425,6 +452,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// radius
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) {
|
||||
if (length - processedBytes - sizeof(newParticle._radius) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius));
|
||||
dataAt += sizeof(newParticle._radius);
|
||||
processedBytes += sizeof(newParticle._radius);
|
||||
|
@ -432,6 +464,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// position
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) {
|
||||
if (length - processedBytes - sizeof(newParticle._position) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._position, dataAt, sizeof(newParticle._position));
|
||||
dataAt += sizeof(newParticle._position);
|
||||
processedBytes += sizeof(newParticle._position);
|
||||
|
@ -439,6 +476,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// color
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) {
|
||||
if (length - processedBytes - sizeof(newParticle._color) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(newParticle._color, dataAt, sizeof(newParticle._color));
|
||||
dataAt += sizeof(newParticle._color);
|
||||
processedBytes += sizeof(newParticle._color);
|
||||
|
@ -446,6 +488,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// velocity
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) {
|
||||
if (length - processedBytes - sizeof(newParticle._velocity) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._velocity, dataAt, sizeof(newParticle._velocity));
|
||||
dataAt += sizeof(newParticle._velocity);
|
||||
processedBytes += sizeof(newParticle._velocity);
|
||||
|
@ -453,6 +500,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// gravity
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) {
|
||||
if (length - processedBytes - sizeof(newParticle._gravity) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._gravity, dataAt, sizeof(newParticle._gravity));
|
||||
dataAt += sizeof(newParticle._gravity);
|
||||
processedBytes += sizeof(newParticle._gravity);
|
||||
|
@ -460,6 +512,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// damping
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) {
|
||||
if (length - processedBytes - sizeof(newParticle._damping) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._damping, dataAt, sizeof(newParticle._damping));
|
||||
dataAt += sizeof(newParticle._damping);
|
||||
processedBytes += sizeof(newParticle._damping);
|
||||
|
@ -467,6 +524,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// lifetime
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) {
|
||||
if (length - processedBytes - sizeof(newParticle._lifetime) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._lifetime, dataAt, sizeof(newParticle._lifetime));
|
||||
dataAt += sizeof(newParticle._lifetime);
|
||||
processedBytes += sizeof(newParticle._lifetime);
|
||||
|
@ -475,6 +537,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
// TODO: make inHand and shouldDie into single bits
|
||||
// inHand
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) {
|
||||
if (length - processedBytes - sizeof(newParticle._inHand) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._inHand, dataAt, sizeof(newParticle._inHand));
|
||||
dataAt += sizeof(newParticle._inHand);
|
||||
processedBytes += sizeof(newParticle._inHand);
|
||||
|
@ -482,6 +549,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// shouldDie
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) {
|
||||
if (length - processedBytes - sizeof(newParticle._shouldDie) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._shouldDie, dataAt, sizeof(newParticle._shouldDie));
|
||||
dataAt += sizeof(newParticle._shouldDie);
|
||||
processedBytes += sizeof(newParticle._shouldDie);
|
||||
|
@ -490,9 +562,20 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
// script
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) {
|
||||
uint16_t scriptLength;
|
||||
if (length - processedBytes - sizeof(scriptLength) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&scriptLength, dataAt, sizeof(scriptLength));
|
||||
dataAt += sizeof(scriptLength);
|
||||
processedBytes += sizeof(scriptLength);
|
||||
|
||||
if (length - processedBytes - scriptLength < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
QString tempString((const char*)dataAt);
|
||||
newParticle._script = tempString;
|
||||
dataAt += scriptLength;
|
||||
|
@ -502,9 +585,20 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
// modelURL
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) {
|
||||
uint16_t modelURLLength;
|
||||
if (length - processedBytes - sizeof(modelURLLength) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&modelURLLength, dataAt, sizeof(modelURLLength));
|
||||
dataAt += sizeof(modelURLLength);
|
||||
processedBytes += sizeof(modelURLLength);
|
||||
|
||||
if (length - processedBytes - modelURLLength < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
QString tempString((const char*)dataAt);
|
||||
newParticle._modelURL = tempString;
|
||||
dataAt += modelURLLength;
|
||||
|
@ -513,6 +607,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// modelScale
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) {
|
||||
if (length - processedBytes - sizeof(newParticle._modelScale) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._modelScale, dataAt, sizeof(newParticle._modelScale));
|
||||
dataAt += sizeof(newParticle._modelScale);
|
||||
processedBytes += sizeof(newParticle._modelScale);
|
||||
|
@ -520,6 +619,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// modelTranslation
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) {
|
||||
if (length - processedBytes - sizeof(newParticle._modelTranslation) < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
memcpy(&newParticle._modelTranslation, dataAt, sizeof(newParticle._modelTranslation));
|
||||
dataAt += sizeof(newParticle._modelTranslation);
|
||||
processedBytes += sizeof(newParticle._modelTranslation);
|
||||
|
@ -527,6 +631,12 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr
|
|||
|
||||
// modelRotation
|
||||
if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) {
|
||||
const int expectedBytesForPackedQuat = sizeof(uint16_t) * 4; // this is how we pack the quats
|
||||
if (length - processedBytes - expectedBytesForPackedQuat < 0) {
|
||||
valid = false;
|
||||
processedBytes = length;
|
||||
return newParticle; // fail as if we read the entire buffer
|
||||
}
|
||||
int bytes = unpackOrientationQuatFromBytes(dataAt, newParticle._modelRotation);
|
||||
dataAt += bytes;
|
||||
processedBytes += bytes;
|
||||
|
|
|
@ -95,3 +95,34 @@ std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) {
|
|||
|
||||
#endif // DEBUG
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
#include <QDebug>
|
||||
|
||||
QDebug& operator<<(QDebug& dbg, const glm::vec3& v) {
|
||||
dbg.nospace() << "{type='glm::vec3'"
|
||||
", x=" << v.x <<
|
||||
", y=" << v.y <<
|
||||
", z=" << v.z <<
|
||||
"}";
|
||||
return dbg;
|
||||
}
|
||||
|
||||
QDebug& operator<<(QDebug& dbg, const glm::quat& q) {
|
||||
dbg.nospace() << "{type='glm::quat'"
|
||||
", x=" << q.x <<
|
||||
", y=" << q.y <<
|
||||
", z=" << q.z <<
|
||||
", w=" << q.w <<
|
||||
"}";
|
||||
return dbg;
|
||||
}
|
||||
|
||||
QDebug& operator<<(QDebug& dbg, const glm::mat4& m) {
|
||||
dbg.nospace() << "{type='glm::mat4', [";
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dbg << ' ' << m[0][j] << ' ' << m[1][j] << ' ' << m[2][j] << ' ' << m[3][j] << ';';
|
||||
}
|
||||
return dbg << " ]}";
|
||||
}
|
||||
|
||||
#endif // QT_NO_DEBUG_STREAM
|
||||
|
|
|
@ -38,13 +38,20 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion);
|
|||
|
||||
// less common utils can be enabled with DEBUG
|
||||
#ifdef DEBUG
|
||||
#include "CollisionInfo.h"
|
||||
#include "SphereShape.h"
|
||||
#include "CapsuleShape.h"
|
||||
class CollisionInfo;
|
||||
class SphereShape;
|
||||
class CapsuleShape;
|
||||
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c);
|
||||
std::ostream& operator<<(std::ostream& s, const SphereShape& shape);
|
||||
std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule);
|
||||
#endif // DEBUG
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
class QDebug;
|
||||
// Add support for writing these to qDebug().
|
||||
QDebug& operator<<(QDebug& s, const glm::vec3& v);
|
||||
QDebug& operator<<(QDebug& s, const glm::quat& q);
|
||||
QDebug& operator<<(QDebug& s, const glm::mat4& m);
|
||||
#endif // QT_NO_DEBUG_STREAM
|
||||
|
||||
#endif // hifi_StreamUtils_h
|
||||
|
|
|
@ -12,25 +12,7 @@
|
|||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "PhysicsTestUtil.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const glm::vec3& v) {
|
||||
s << "<" << v.x << "," << v.y << "," << v.z << ">";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const glm::quat& q) {
|
||||
s << "<" << q.x << "," << q.y << "," << q.z << "," << q.w << ">";
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const glm::mat4& m) {
|
||||
s << "[";
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
s << " " << m[0][j] << " " << m[1][j] << " " << m[2][j] << " " << m[3][j] << ";";
|
||||
}
|
||||
s << " ]";
|
||||
return s;
|
||||
}
|
||||
#include "StreamUtils.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) {
|
||||
s << "[penetration=" << c._penetration
|
||||
|
|
|
@ -21,9 +21,6 @@ const glm::vec3 xAxis(1.f, 0.f, 0.f);
|
|||
const glm::vec3 yAxis(0.f, 1.f, 0.f);
|
||||
const glm::vec3 zAxis(0.f, 0.f, 1.f);
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const glm::vec3& v);
|
||||
std::ostream& operator<<(std::ostream& s, const glm::quat& q);
|
||||
std::ostream& operator<<(std::ostream& s, const glm::mat4& m);
|
||||
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c);
|
||||
|
||||
#endif // hifi_PhysicsTestUtil_h
|
||||
|
|
Loading…
Reference in a new issue