mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 07:15:30 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels
Conflicts: libraries/metavoxels/src/Spanner.cpp
This commit is contained in:
commit
e3074d726b
33 changed files with 171 additions and 167 deletions
assignment-client/src
domain-server/src
interface/src
libraries
audio/src
avatars/src
entities-renderer/src
entities/src
DeleteEntityOperator.cppDeleteEntityOperator.hEntityItem.cppEntitySimulation.cppEntitySimulation.hEntityTree.cppEntityTree.h
networking/src
physics/src
script-engine/src
tests/octree/src
tools/scribe/src
|
@ -184,6 +184,7 @@ void Agent::run() {
|
|||
loop.exec();
|
||||
|
||||
QString scriptContents(reply->readAll());
|
||||
delete reply;
|
||||
|
||||
qDebug() << "Downloaded script:" << scriptContents;
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
#include "AudioMixer.h"
|
||||
|
||||
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
|
||||
const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18;
|
||||
const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18f;
|
||||
const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.003f;
|
||||
const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
|
||||
const QString AUDIO_ENV_GROUP_KEY = "audio_env";
|
||||
|
|
|
@ -1736,6 +1736,9 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
|
|||
connection->respond(HTTPConnection::StatusCode302, QByteArray(),
|
||||
HTTPConnection::DefaultContentType, cookieHeaders);
|
||||
|
||||
delete tokenReply;
|
||||
delete profileReply;
|
||||
|
||||
// we've redirected the user back to our homepage
|
||||
return true;
|
||||
|
||||
|
|
|
@ -3770,9 +3770,9 @@ void Application::parseVersionXml() {
|
|||
QString latestVersion;
|
||||
QUrl downloadUrl;
|
||||
QString releaseNotes("Unavailable");
|
||||
QObject* sender = QObject::sender();
|
||||
QNetworkReply* sender = qobject_cast<QNetworkReply*>(QObject::sender());
|
||||
|
||||
QXmlStreamReader xml(qobject_cast<QNetworkReply*>(sender));
|
||||
QXmlStreamReader xml(sender);
|
||||
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) {
|
||||
|
|
|
@ -454,47 +454,6 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode, bool
|
|||
return;
|
||||
}
|
||||
renderDisplayName();
|
||||
|
||||
if (!_chatMessage.empty()) {
|
||||
int width = 0;
|
||||
int lastWidth = 0;
|
||||
for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) {
|
||||
width += (lastWidth = textRenderer(CHAT)->computeWidth(*it));
|
||||
}
|
||||
glPushMatrix();
|
||||
|
||||
glm::vec3 chatPosition = getHead()->getEyePosition() + getBodyUpDirection() * CHAT_MESSAGE_HEIGHT * _scale;
|
||||
glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z);
|
||||
glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation();
|
||||
glm::vec3 chatAxis = glm::axis(chatRotation);
|
||||
glRotatef(glm::degrees(glm::angle(chatRotation)), chatAxis.x, chatAxis.y, chatAxis.z);
|
||||
|
||||
glColor3f(0.0f, 0.8f, 0.0f);
|
||||
glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
|
||||
glRotatef(180.0f, 0.0f, 0.0f, 1.0f);
|
||||
glScalef(_scale * CHAT_MESSAGE_SCALE, _scale * CHAT_MESSAGE_SCALE, 1.0f);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glDepthMask(false);
|
||||
if (_keyState == NO_KEY_DOWN) {
|
||||
textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
|
||||
} else {
|
||||
// rather than using substr and allocating a new string, just replace the last
|
||||
// character with a null, then restore it
|
||||
int lastIndex = _chatMessage.size() - 1;
|
||||
char lastChar = _chatMessage[lastIndex];
|
||||
_chatMessage[lastIndex] = '\0';
|
||||
textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str());
|
||||
_chatMessage[lastIndex] = lastChar;
|
||||
glColor3f(0.0f, 1.0f, 0.0f);
|
||||
textRenderer(CHAT)->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex);
|
||||
}
|
||||
glEnable(GL_LIGHTING);
|
||||
glDepthMask(true);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
||||
|
|
|
@ -49,16 +49,22 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
|
|||
Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar);
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||
state.setRotationInConstrainedFrame(
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * (_owningHead->getFinalRoll() - owningAvatar->getHead()->getFinalLeanSideways()),
|
||||
glm::normalize(inverse * axes[2]))
|
||||
* glm::angleAxis(RADIANS_PER_DEGREE * (_owningHead->getFinalYaw() - _owningHead->getTorsoTwist()),
|
||||
glm::normalize(inverse * axes[1]))
|
||||
* glm::angleAxis(- RADIANS_PER_DEGREE * (_owningHead->getFinalPitch() - owningAvatar->getHead()->getFinalLeanForward()),
|
||||
glm::normalize(inverse * axes[0]))
|
||||
* joint.rotation, DEFAULT_PRIORITY);
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
|
||||
glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||
glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame());
|
||||
if (owningAvatar->isMyAvatar()) {
|
||||
glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(),
|
||||
_owningHead->getTorsoTwist(),
|
||||
_owningHead->getFinalLeanSideways()));
|
||||
pitchYawRoll -= lean;
|
||||
}
|
||||
|
||||
state.setRotationInConstrainedFrame(glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
|
||||
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
|
||||
* glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
|
||||
* joint.rotation, DEFAULT_PRIORITY);
|
||||
|
||||
}
|
||||
|
||||
void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
|
|
|
@ -158,6 +158,8 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
|
|||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
_scriptEditorWidgetUI->scriptEdit->setPlainText(reply->readAll());
|
||||
delete reply;
|
||||
|
||||
if (!saveAs()) {
|
||||
static_cast<ScriptEditorWindow*>(this->parent()->parent()->parent())->terminateCurrentTab();
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ void SnapshotShareDialog::postRequestFinished() {
|
|||
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
requestReply->deleteLater();
|
||||
const QJsonObject& responseObject = jsonResponse.object();
|
||||
|
||||
if (responseObject.contains("id")) {
|
||||
|
|
|
@ -205,6 +205,7 @@ void BillboardOverlay::replyFinished() {
|
|||
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
|
||||
_billboard = reply->readAll();
|
||||
_isLoaded = true;
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
|
|
@ -66,6 +66,7 @@ void ImageOverlay::replyFinished() {
|
|||
_textureImage.loadFromData(rawData);
|
||||
_renderImage = true;
|
||||
_isLoaded = true;
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void ImageOverlay::render(RenderArgs* args) {
|
||||
|
|
|
@ -26,8 +26,8 @@ namespace AudioConstants {
|
|||
const int NETWORK_FRAME_BYTES_PER_CHANNEL = 512;
|
||||
const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample);
|
||||
const float NETWORK_FRAME_MSECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL
|
||||
/ (float) AudioConstants::SAMPLE_RATE) * 1000.0;
|
||||
const unsigned int NETWORK_FRAME_USECS = floorf(NETWORK_FRAME_MSECS * 1000.0);
|
||||
/ (float)AudioConstants::SAMPLE_RATE) * 1000.0f;
|
||||
const unsigned int NETWORK_FRAME_USECS = (unsigned int)floorf(NETWORK_FRAME_MSECS * 1000.0f);
|
||||
const int MIN_SAMPLE_VALUE = std::numeric_limits<AudioSample>::min();
|
||||
const int MAX_SAMPLE_VALUE = std::numeric_limits<AudioSample>::max();
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ void Sound::downloadFinished(QNetworkReply* reply) {
|
|||
}
|
||||
|
||||
_isReady = true;
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void Sound::downSample(const QByteArray& rawAudioByteArray) {
|
||||
|
|
|
@ -175,11 +175,6 @@ QByteArray AvatarData::toByteArray() {
|
|||
// Instantaneous audio loudness (used to drive facial animation)
|
||||
memcpy(destinationBuffer, &_headData->_audioLoudness, sizeof(float));
|
||||
destinationBuffer += sizeof(float);
|
||||
|
||||
// chat message
|
||||
*destinationBuffer++ = _chatMessage.size();
|
||||
memcpy(destinationBuffer, _chatMessage.data(), _chatMessage.size() * sizeof(char));
|
||||
destinationBuffer += _chatMessage.size() * sizeof(char);
|
||||
|
||||
// bitMask of less than byte wide items
|
||||
unsigned char bitItems = 0;
|
||||
|
@ -300,11 +295,10 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
|
|||
// lookAt = 12
|
||||
// audioLoudness = 4
|
||||
// }
|
||||
// + 1 byte for messageSize (0)
|
||||
// + 1 byte for pupilSize
|
||||
// + 1 byte for numJoints (0)
|
||||
// = 53 bytes
|
||||
int minPossibleSize = 53;
|
||||
int minPossibleSize = 52;
|
||||
|
||||
int maxAvailableSize = packet.size() - offset;
|
||||
if (minPossibleSize > maxAvailableSize) {
|
||||
|
@ -420,23 +414,6 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) {
|
|||
_headData->_audioLoudness = audioLoudness;
|
||||
} // 4 bytes
|
||||
|
||||
// chat
|
||||
int chatMessageSize = *sourceBuffer++;
|
||||
minPossibleSize += chatMessageSize;
|
||||
if (minPossibleSize > maxAvailableSize) {
|
||||
if (shouldLogError(now)) {
|
||||
qDebug() << "Malformed AvatarData packet before ChatMessage;"
|
||||
<< " displayName = '" << _displayName << "'"
|
||||
<< " minPossibleSize = " << minPossibleSize
|
||||
<< " maxAvailableSize = " << maxAvailableSize;
|
||||
}
|
||||
return maxAvailableSize;
|
||||
}
|
||||
{ // chat payload
|
||||
_chatMessage = string((char*)sourceBuffer, chatMessageSize);
|
||||
sourceBuffer += chatMessageSize * sizeof(char);
|
||||
} // 1 + chatMessageSize bytes
|
||||
|
||||
{ // bitFlags and face data
|
||||
unsigned char bitItems = *sourceBuffer++;
|
||||
|
||||
|
|
|
@ -32,17 +32,17 @@ typedef unsigned long long quint64;
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QVariantMap>
|
||||
#include <QByteArray>
|
||||
#include <QElapsedTimer>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QRect>
|
||||
#include <QScriptable>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <QVariantMap>
|
||||
#include <QVector>
|
||||
|
||||
#include <CollisionInfo.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
@ -130,7 +130,6 @@ class AvatarData : public QObject {
|
|||
Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw)
|
||||
Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch)
|
||||
Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll)
|
||||
Q_PROPERTY(QString chatMessage READ getQStringChatMessage WRITE setChatMessage)
|
||||
|
||||
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
|
||||
Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation)
|
||||
|
@ -242,12 +241,6 @@ public:
|
|||
void setKeyState(KeyState s) { _keyState = s; }
|
||||
KeyState keyState() const { return _keyState; }
|
||||
|
||||
// chat message
|
||||
void setChatMessage(const std::string& msg) { _chatMessage = msg; }
|
||||
void setChatMessage(const QString& string) { _chatMessage = string.toLocal8Bit().constData(); }
|
||||
const std::string& setChatMessage() const { return _chatMessage; }
|
||||
QString getQStringChatMessage() { return QString(_chatMessage.data()); }
|
||||
|
||||
bool isChatCirclingEnabled() const { return _isChatCirclingEnabled; }
|
||||
const HeadData* getHeadData() const { return _headData; }
|
||||
const HandData* getHandData() const { return _handData; }
|
||||
|
@ -355,9 +348,6 @@ protected:
|
|||
// key state
|
||||
KeyState _keyState;
|
||||
|
||||
// chat message
|
||||
std::string _chatMessage;
|
||||
|
||||
bool _isChatCirclingEnabled;
|
||||
bool _forceFaceshiftConnected;
|
||||
bool _hasNewJointRotations; // set in AvatarData, cleared in Avatar
|
||||
|
@ -396,7 +386,6 @@ private:
|
|||
AvatarData(const AvatarData&);
|
||||
AvatarData& operator= (const AvatarData&);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AvatarData*)
|
||||
|
||||
class JointData {
|
||||
|
|
|
@ -145,6 +145,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe
|
|||
} else {
|
||||
qDebug() << "ERROR Loading file:" << url.toString();
|
||||
}
|
||||
delete reply;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,13 +91,9 @@ bool DeleteEntityOperator::preRecursion(OctreeElement* element) {
|
|||
// If this is the element we're looking for, then ask it to remove the old entity
|
||||
// and we can stop searching.
|
||||
if (entityTreeElement == details.containingElement) {
|
||||
EntityItemID entityItemID = details.entity->getEntityItemID();
|
||||
EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity
|
||||
assert(theEntity);
|
||||
_tree->trackDeletedEntity(theEntity);
|
||||
entityTreeElement->removeEntityItem(theEntity); // remove it from the element
|
||||
_tree->setContainingElement(entityItemID, NULL); // update or id to element lookup
|
||||
delete theEntity; // now actually delete the entity!
|
||||
EntityItem* theEntity = details.entity;
|
||||
assert(entityTreeElement->removeEntityItem(theEntity)); // remove it from the element
|
||||
_tree->setContainingElement(details.entity->getEntityItemID(), NULL); // update or id to element lookup
|
||||
_foundCount++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,13 @@
|
|||
|
||||
class EntityToDeleteDetails {
|
||||
public:
|
||||
const EntityItem* entity;
|
||||
EntityItem* entity;
|
||||
AACube cube;
|
||||
EntityTreeElement* containingElement;
|
||||
};
|
||||
|
||||
typedef QSet<EntityToDeleteDetails> RemovedEntities;
|
||||
|
||||
inline uint qHash(const EntityToDeleteDetails& a, uint seed) {
|
||||
return qHash(a.entity->getEntityItemID(), seed);
|
||||
}
|
||||
|
@ -36,9 +38,11 @@ public:
|
|||
void addEntityIDToDeleteList(const EntityItemID& searchEntityID);
|
||||
virtual bool preRecursion(OctreeElement* element);
|
||||
virtual bool postRecursion(OctreeElement* element);
|
||||
|
||||
const RemovedEntities& getEntities() const { return _entitiesToDelete; }
|
||||
private:
|
||||
EntityTree* _tree;
|
||||
QSet<EntityToDeleteDetails> _entitiesToDelete;
|
||||
RemovedEntities _entitiesToDelete;
|
||||
quint64 _changeTime;
|
||||
int _foundCount;
|
||||
int _lookingCount;
|
||||
|
|
|
@ -1019,7 +1019,6 @@ void EntityItem::recalculateCollisionShape() {
|
|||
}
|
||||
|
||||
const float MIN_POSITION_DELTA = 0.0001f;
|
||||
const float MIN_DIMENSION_DELTA = 0.0001f;
|
||||
const float MIN_ALIGNMENT_DOT = 0.9999f;
|
||||
const float MIN_MASS_DELTA = 0.001f;
|
||||
const float MIN_VELOCITY_DELTA = 0.025f;
|
||||
|
@ -1045,17 +1044,17 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) {
|
|||
}
|
||||
|
||||
void EntityItem::updateDimensions(const glm::vec3& value) {
|
||||
if (glm::distance(_dimensions, value) * (float)TREE_SCALE > MIN_DIMENSION_DELTA) {
|
||||
_dimensions = value;
|
||||
if (_dimensions != value) {
|
||||
_dimensions = glm::abs(value);
|
||||
recalculateCollisionShape();
|
||||
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::updateDimensionsInMeters(const glm::vec3& value) {
|
||||
glm::vec3 dimensions = value / (float) TREE_SCALE;
|
||||
if (glm::distance(_dimensions, dimensions) * (float)TREE_SCALE > MIN_DIMENSION_DELTA) {
|
||||
_dimensions = dimensions;
|
||||
glm::vec3 dimensions = glm::abs(value) / (float) TREE_SCALE;
|
||||
if (_dimensions != dimensions) {
|
||||
_dimensions = dimensions;
|
||||
recalculateCollisionShape();
|
||||
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ void EntitySimulation::updateEntities(QSet<EntityItem*>& entitiesToDelete) {
|
|||
_entitiesToDelete.clear();
|
||||
}
|
||||
|
||||
// private
|
||||
void EntitySimulation::expireMortalEntities(const quint64& now) {
|
||||
if (now > _nextExpiry) {
|
||||
// only search for expired entities if we expect to find one
|
||||
|
@ -63,6 +64,7 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
|
|||
}
|
||||
}
|
||||
|
||||
// private
|
||||
void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
||||
PerformanceTimer perfTimer("updatingEntities");
|
||||
QSet<EntityItem*>::iterator itemItr = _updateableEntities.begin();
|
||||
|
@ -79,6 +81,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
|||
}
|
||||
}
|
||||
|
||||
// private
|
||||
void EntitySimulation::sortEntitiesThatMoved() {
|
||||
// NOTE: this is only for entities that have been moved by THIS EntitySimulation.
|
||||
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
||||
|
|
|
@ -33,9 +33,12 @@ const int DIRTY_SIMULATION_FLAGS =
|
|||
|
||||
class EntitySimulation {
|
||||
public:
|
||||
EntitySimulation() : _entityTree(NULL) { }
|
||||
EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL) { }
|
||||
virtual ~EntitySimulation() { setEntityTree(NULL); }
|
||||
|
||||
void lock() { _mutex.lock(); }
|
||||
void unlock() { _mutex.unlock(); }
|
||||
|
||||
/// \param tree pointer to EntityTree which is stored internally
|
||||
void setEntityTree(EntityTree* tree);
|
||||
|
||||
|
@ -80,6 +83,8 @@ protected:
|
|||
void callUpdateOnEntitiesThatNeedIt(const quint64& now);
|
||||
void sortEntitiesThatMoved();
|
||||
|
||||
QMutex _mutex;
|
||||
|
||||
// back pointer to EntityTree structure
|
||||
EntityTree* _entityTree;
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "EntitySimulation.h"
|
||||
|
||||
#include "AddEntityOperator.h"
|
||||
#include "DeleteEntityOperator.h"
|
||||
#include "MovingEntitiesOperator.h"
|
||||
#include "UpdateEntityOperator.h"
|
||||
|
||||
|
@ -40,7 +39,9 @@ EntityTreeElement* EntityTree::createNewElement(unsigned char * octalCode) {
|
|||
void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
||||
// this would be a good place to clean up our entities...
|
||||
if (_simulation) {
|
||||
_simulation->lock();
|
||||
_simulation->clearEntities();
|
||||
_simulation->unlock();
|
||||
}
|
||||
foreach (EntityTreeElement* element, _entityToElementMap) {
|
||||
element->cleanupEntities();
|
||||
|
@ -84,7 +85,9 @@ void EntityTree::postAddEntity(EntityItem* entity) {
|
|||
assert(entity);
|
||||
// check to see if we need to simulate this entity..
|
||||
if (_simulation) {
|
||||
_simulation->lock();
|
||||
_simulation->addEntity(entity);
|
||||
_simulation->unlock();
|
||||
}
|
||||
_isDirty = true;
|
||||
emit addingEntity(entity->getEntityItemID());
|
||||
|
@ -141,7 +144,9 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
|
|||
if (newFlags) {
|
||||
if (_simulation) {
|
||||
if (newFlags & DIRTY_SIMULATION_FLAGS) {
|
||||
_simulation->lock();
|
||||
_simulation->entityChanged(entity);
|
||||
_simulation->unlock();
|
||||
}
|
||||
} else {
|
||||
// normally the _simulation clears ALL updateFlags, but since there is none we do it explicitly
|
||||
|
@ -204,21 +209,6 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
void EntityTree::trackDeletedEntity(EntityItem* entity) {
|
||||
if (_simulation) {
|
||||
_simulation->removeEntity(entity);
|
||||
}
|
||||
// this is only needed on the server to send delete messages for recently deleted entities to the viewers
|
||||
if (getIsServer()) {
|
||||
// set up the deleted entities ID
|
||||
quint64 deletedAt = usecTimestampNow();
|
||||
_recentlyDeletedEntitiesLock.lockForWrite();
|
||||
_recentlyDeletedEntityItemIDs.insert(deletedAt, entity->getEntityItemID().id);
|
||||
_recentlyDeletedEntitiesLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) {
|
||||
emit entityScriptChanging(entityItemID);
|
||||
}
|
||||
|
@ -231,7 +221,9 @@ void EntityTree::setSimulation(EntitySimulation* simulation) {
|
|||
if (_simulation && _simulation != simulation) {
|
||||
// It's important to clearEntities() on the simulation since taht will update each
|
||||
// EntityItem::_simulationState correctly so as to not confuse the next _simulation.
|
||||
_simulation->lock();
|
||||
_simulation->clearEntities();
|
||||
_simulation->unlock();
|
||||
}
|
||||
_simulation = simulation;
|
||||
}
|
||||
|
@ -242,6 +234,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID) {
|
|||
// NOTE: callers must lock the tree before using this method
|
||||
DeleteEntityOperator theOperator(this, entityID);
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
processRemovedEntities(theOperator);
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
|
@ -255,9 +248,36 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs) {
|
|||
}
|
||||
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
processRemovedEntities(theOperator);
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) {
|
||||
const RemovedEntities& entities = theOperator.getEntities();
|
||||
if (_simulation) {
|
||||
_simulation->lock();
|
||||
}
|
||||
foreach(const EntityToDeleteDetails& details, entities) {
|
||||
EntityItem* theEntity = details.entity;
|
||||
|
||||
if (getIsServer()) {
|
||||
// set up the deleted entities ID
|
||||
quint64 deletedAt = usecTimestampNow();
|
||||
_recentlyDeletedEntitiesLock.lockForWrite();
|
||||
_recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID().id);
|
||||
_recentlyDeletedEntitiesLock.unlock();
|
||||
}
|
||||
|
||||
if (_simulation) {
|
||||
_simulation->removeEntity(theEntity);
|
||||
}
|
||||
delete theEntity; // now actually delete the entity!
|
||||
}
|
||||
if (_simulation) {
|
||||
_simulation->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/// This method is used to find and fix entity IDs that are shifting from creator token based to known ID based entity IDs.
|
||||
/// This should only be used on a client side (viewing) tree. The typical usage is that a local editor has been creating
|
||||
/// entities in the local tree, those entities have creatorToken based entity IDs. But those entity edits are also sent up to
|
||||
|
@ -592,7 +612,9 @@ void EntityTree::releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncod
|
|||
|
||||
void EntityTree::entityChanged(EntityItem* entity) {
|
||||
if (_simulation) {
|
||||
_simulation->lock();
|
||||
_simulation->entityChanged(entity);
|
||||
_simulation->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,7 +622,9 @@ void EntityTree::update() {
|
|||
if (_simulation) {
|
||||
lockForWrite();
|
||||
QSet<EntityItem*> entitiesToDelete;
|
||||
_simulation->lock();
|
||||
_simulation->updateEntities(entitiesToDelete);
|
||||
_simulation->unlock();
|
||||
if (entitiesToDelete.size() > 0) {
|
||||
// translate into list of ID's
|
||||
QSet<EntityItemID> idsToDelete;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <Octree.h>
|
||||
#include "EntityTreeElement.h"
|
||||
#include "DeleteEntityOperator.h"
|
||||
|
||||
|
||||
class Model;
|
||||
|
@ -146,8 +147,6 @@ public:
|
|||
|
||||
void entityChanged(EntityItem* entity);
|
||||
|
||||
void trackDeletedEntity(EntityItem* entity);
|
||||
|
||||
void emitEntityScriptChanging(const EntityItemID& entityItemID);
|
||||
|
||||
void setSimulation(EntitySimulation* simulation);
|
||||
|
@ -160,6 +159,7 @@ signals:
|
|||
|
||||
private:
|
||||
|
||||
void processRemovedEntities(const DeleteEntityOperator& theOperator);
|
||||
bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties,
|
||||
EntityTreeElement* containingElement);
|
||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
|
|
|
@ -240,6 +240,7 @@ void DomainHandler::settingsRequestFinished() {
|
|||
requestDomainSettings();
|
||||
}
|
||||
}
|
||||
settingsReply->deleteLater();
|
||||
}
|
||||
|
||||
void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) {
|
||||
|
|
|
@ -57,7 +57,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
case PacketTypeInjectAudio:
|
||||
return 1;
|
||||
case PacketTypeAvatarData:
|
||||
return 3;
|
||||
return 4;
|
||||
case PacketTypeAvatarIdentity:
|
||||
return 1;
|
||||
case PacketTypeEnvironmentData:
|
||||
|
|
|
@ -67,13 +67,27 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
|
|||
_resources.insert(url, resource);
|
||||
|
||||
} else {
|
||||
_unusedResources.remove(resource->getLRUKey());
|
||||
removeUnusedResource(resource);
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
|
||||
static const int BYTES_PER_MEGABYTES = 1024 * 1024;
|
||||
const int RETAINED_RESOURCE_COUNT = 50;
|
||||
const int RETAINED_RESOURCE_SIZE = 100 * BYTES_PER_MEGABYTES;
|
||||
|
||||
while (_unusedResourcesTotalBytes + resource->getBytesTotal() > RETAINED_RESOURCE_SIZE &&
|
||||
!_unusedResources.empty()) {
|
||||
// unload the oldest resource
|
||||
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
|
||||
|
||||
_unusedResourcesTotalBytes -= it.value()->getBytesTotal();
|
||||
it.value()->setCache(NULL);
|
||||
_unusedResources.erase(it);
|
||||
}
|
||||
|
||||
|
||||
if (_unusedResources.size() > RETAINED_RESOURCE_COUNT) {
|
||||
// unload the oldest resource
|
||||
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
|
||||
|
@ -82,6 +96,14 @@ void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource)
|
|||
}
|
||||
resource->setLRUKey(++_lastLRUKey);
|
||||
_unusedResources.insert(resource->getLRUKey(), resource);
|
||||
_unusedResourcesTotalBytes += resource->getBytesTotal();
|
||||
}
|
||||
|
||||
void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resource) {
|
||||
if (_unusedResources.contains(resource->getLRUKey())) {
|
||||
_unusedResources.remove(resource->getLRUKey());
|
||||
_unusedResourcesTotalBytes -= resource->getBytesTotal();
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceCache::attemptRequest(Resource* resource) {
|
||||
|
|
|
@ -45,7 +45,8 @@ public:
|
|||
void refresh(const QUrl& url);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
qint64 _unusedResourcesTotalBytes = 0;
|
||||
QMap<int, QSharedPointer<Resource> > _unusedResources;
|
||||
|
||||
/// Loads a resource from the specified URL.
|
||||
|
@ -58,8 +59,9 @@ protected:
|
|||
/// Creates a new resource.
|
||||
virtual QSharedPointer<Resource> createResource(const QUrl& url,
|
||||
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) = 0;
|
||||
|
||||
|
||||
void addUnusedResource(const QSharedPointer<Resource>& resource);
|
||||
void removeUnusedResource(const QSharedPointer<Resource>& resource);
|
||||
|
||||
static void attemptRequest(Resource* resource);
|
||||
static void requestCompleted(Resource* resource);
|
||||
|
|
|
@ -169,7 +169,7 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) {
|
|||
_collisionDispatcher = new btCollisionDispatcher(_collisionConfig);
|
||||
_broadphaseFilter = new btDbvtBroadphase();
|
||||
_constraintSolver = new btSequentialImpulseConstraintSolver;
|
||||
_dynamicsWorld = new ThreadSafeDynamicsWorld(_collisionDispatcher, _broadphaseFilter, _constraintSolver, _collisionConfig, _entityTree);
|
||||
_dynamicsWorld = new ThreadSafeDynamicsWorld(_collisionDispatcher, _broadphaseFilter, _constraintSolver, _collisionConfig);
|
||||
|
||||
// default gravity of the world is zero, so each object must specify its own gravity
|
||||
// TODO: set up gravity zones
|
||||
|
@ -200,13 +200,14 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) {
|
|||
const float FIXED_SUBSTEP = 1.0f / 60.0f;
|
||||
|
||||
void PhysicsEngine::stepSimulation() {
|
||||
lock();
|
||||
// NOTE: the grand order of operations is:
|
||||
// (1) relay incoming changes
|
||||
// (2) step simulation
|
||||
// (3) synchronize outgoing motion states
|
||||
// (4) send outgoing packets
|
||||
|
||||
// this is step (1)
|
||||
// This is step (1).
|
||||
relayIncomingChangesToSimulation();
|
||||
|
||||
const int MAX_NUM_SUBSTEPS = 4;
|
||||
|
@ -215,9 +216,24 @@ void PhysicsEngine::stepSimulation() {
|
|||
_clock.reset();
|
||||
float timeStep = btMin(dt, MAX_TIMESTEP);
|
||||
|
||||
// steps (2) and (3) are performed by ThreadSafeDynamicsWorld::stepSimulation())
|
||||
// This is step (2).
|
||||
int numSubSteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, FIXED_SUBSTEP);
|
||||
_frameCount += (uint32_t)numSubSteps;
|
||||
unlock();
|
||||
|
||||
// This is step (3) which is done outside of stepSimulation() so we can lock _entityTree.
|
||||
//
|
||||
// Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree
|
||||
// to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this
|
||||
// PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own
|
||||
// lock on the tree before we re-lock ourselves.
|
||||
//
|
||||
// TODO: untangle these lock sequences.
|
||||
_entityTree->lockForWrite();
|
||||
lock();
|
||||
_dynamicsWorld->synchronizeMotionStates();
|
||||
unlock();
|
||||
_entityTree->unlock();
|
||||
}
|
||||
|
||||
// Bullet collision flags are as follows:
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
* Copied and modified from btDiscreteDynamicsWorld.cpp by AndrewMeadows on 2014.11.12.
|
||||
* */
|
||||
|
||||
#include <EntityTree.h>
|
||||
|
||||
#include "ThreadSafeDynamicsWorld.h"
|
||||
|
||||
#ifdef USE_BULLET_PHYSICS
|
||||
|
@ -24,17 +22,8 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
|||
btDispatcher* dispatcher,
|
||||
btBroadphaseInterface* pairCache,
|
||||
btConstraintSolver* constraintSolver,
|
||||
btCollisionConfiguration* collisionConfiguration,
|
||||
EntityTree* entities)
|
||||
btCollisionConfiguration* collisionConfiguration)
|
||||
: btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) {
|
||||
assert(entities);
|
||||
_entities = entities;
|
||||
}
|
||||
|
||||
void ThreadSafeDynamicsWorld::synchronizeMotionStates() {
|
||||
_entities->lockForWrite();
|
||||
btDiscreteDynamicsWorld::synchronizeMotionStates();
|
||||
_entities->unlock();
|
||||
}
|
||||
|
||||
int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) {
|
||||
|
@ -82,10 +71,15 @@ int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps,
|
|||
}
|
||||
}
|
||||
|
||||
// We only sync motion states once at the end of all substeps.
|
||||
// This is to avoid placing multiple, repeated thread locks on _entities.
|
||||
synchronizeMotionStates();
|
||||
// NOTE: We do NOT call synchronizeMotionState() after each substep (to avoid multiple locks on the
|
||||
// object data outside of the physics engine). A consequence of this is that the transforms of the
|
||||
// external objects only ever update at the end of the full step.
|
||||
|
||||
// NOTE: We do NOT call synchronizeMotionStates() here. Instead it is called by an external class
|
||||
// that knows how to lock threads correctly.
|
||||
|
||||
clearForces();
|
||||
return subSteps;
|
||||
|
||||
return subSteps;
|
||||
}
|
||||
#endif // USE_BULLET_PHYSICS
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
#ifdef USE_BULLET_PHYSICS
|
||||
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
|
||||
|
||||
class EntityTree;
|
||||
|
||||
ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld {
|
||||
public:
|
||||
BT_DECLARE_ALIGNED_ALLOCATOR();
|
||||
|
@ -31,20 +29,15 @@ public:
|
|||
btDispatcher* dispatcher,
|
||||
btBroadphaseInterface* pairCache,
|
||||
btConstraintSolver* constraintSolver,
|
||||
btCollisionConfiguration* collisionConfiguration,
|
||||
EntityTree* entities);
|
||||
btCollisionConfiguration* collisionConfiguration);
|
||||
|
||||
// virtual overrides from btDiscreteDynamicsWorld
|
||||
int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.));
|
||||
void synchronizeMotionStates();
|
||||
|
||||
// btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated
|
||||
// but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide
|
||||
// smoother rendering of objects when the physics simulation loop is ansynchronous to the render loop).
|
||||
float getLocalTimeAccumulation() const { return m_localTime; }
|
||||
|
||||
private:
|
||||
EntityTree* _entities;
|
||||
};
|
||||
|
||||
#else // USE_BULLET_PHYSICS
|
||||
|
|
|
@ -194,6 +194,8 @@ void ScriptEngine::handleScriptDownload() {
|
|||
qDebug() << "ERROR Loading file:" << reply->url().toString();
|
||||
emit errorLoadingScript(_fileNameString);
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void ScriptEngine::init() {
|
||||
|
@ -605,6 +607,7 @@ void ScriptEngine::include(const QString& includeFile) {
|
|||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
includeContents = reply->readAll();
|
||||
reply->deleteLater();
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
QString fileName = url.toString();
|
||||
|
|
|
@ -332,7 +332,7 @@ void XMLHttpRequestClass::abortRequest() {
|
|||
if (_reply) {
|
||||
disconnectFromReply(_reply);
|
||||
_reply->abort();
|
||||
delete _reply;
|
||||
_reply->deleteLater();
|
||||
_reply = NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -802,7 +802,7 @@ void OctreeTests::propertyFlagsTests(bool verbose) {
|
|||
qDebug() << "fill encoded byte array with extra garbage (as if it was bitstream with more content)";
|
||||
}
|
||||
QByteArray extraContent;
|
||||
extraContent.fill(0xba, 10);
|
||||
extraContent.fill(0xbaU, 10);
|
||||
encoded.append(extraContent);
|
||||
|
||||
if (verbose) {
|
||||
|
|
|
@ -837,7 +837,7 @@ int TextTemplate::evalBlockGeneration(std::ostream& dst, const BlockPointer& blo
|
|||
if (block->command.arguments.size()) {
|
||||
// THe actual value of the var defined sneeds to be evaluated:
|
||||
String val;
|
||||
for (int t = 1; t < block->command.arguments.size(); t++) {
|
||||
for (unsigned int t = 1; t < block->command.arguments.size(); t++) {
|
||||
// detect if a param is a var
|
||||
int len = block->command.arguments[t].length();
|
||||
if ((block->command.arguments[t][0] == Tag::VAR)
|
||||
|
|
Loading…
Reference in a new issue