mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 10:03:58 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into team-teaching
This commit is contained in:
commit
20ceb72d92
38 changed files with 688 additions and 281 deletions
|
@ -287,6 +287,18 @@
|
||||||
allSections.push(elWebSections);
|
allSections.push(elWebSections);
|
||||||
var elWebSourceURL = document.getElementById("property-web-source-url");
|
var elWebSourceURL = document.getElementById("property-web-source-url");
|
||||||
|
|
||||||
|
var elParticleSections = document.querySelectorAll(".particle-section");
|
||||||
|
allSections.push(elParticleSections);
|
||||||
|
var elParticleMaxParticles = document.getElementById("property-particle-maxparticles");
|
||||||
|
var elParticleLifeSpan = document.getElementById("property-particle-lifespan");
|
||||||
|
var elParticleEmitRate = document.getElementById("property-particle-emit-rate");
|
||||||
|
var elParticleEmitDirectionX = document.getElementById("property-particle-emit-direction-x");
|
||||||
|
var elParticleEmitDirectionY = document.getElementById("property-particle-emit-direction-y");
|
||||||
|
var elParticleEmitDirectionZ = document.getElementById("property-particle-emit-direction-z");
|
||||||
|
var elParticleEmitStrength = document.getElementById("property-particle-emit-strength");
|
||||||
|
var elParticleLocalGravity = document.getElementById("property-particle-localgravity");
|
||||||
|
var elParticleRadius = document.getElementById("property-particle-radius");
|
||||||
|
|
||||||
var elTextSections = document.querySelectorAll(".text-section");
|
var elTextSections = document.querySelectorAll(".text-section");
|
||||||
allSections.push(elTextSections);
|
allSections.push(elTextSections);
|
||||||
var elTextText = document.getElementById("property-text-text");
|
var elTextText = document.getElementById("property-text-text");
|
||||||
|
@ -455,7 +467,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.type == "Box" || properties.type == "Sphere") {
|
if (properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
|
||||||
elColorSection.style.display = 'block';
|
elColorSection.style.display = 'block';
|
||||||
elColorRed.value = properties.color.red;
|
elColorRed.value = properties.color.red;
|
||||||
elColorGreen.value = properties.color.green;
|
elColorGreen.value = properties.color.green;
|
||||||
|
@ -465,7 +477,7 @@
|
||||||
elColorSection.style.display = 'none';
|
elColorSection.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.type == "Model") {
|
if (properties.type == "Model" || properties.type == "ParticleEffect") {
|
||||||
for (var i = 0; i < elModelSections.length; i++) {
|
for (var i = 0; i < elModelSections.length; i++) {
|
||||||
elModelSections[i].style.display = 'block';
|
elModelSections[i].style.display = 'block';
|
||||||
}
|
}
|
||||||
|
@ -479,7 +491,7 @@
|
||||||
elModelAnimationFrame.value = properties.animationFrameIndex;
|
elModelAnimationFrame.value = properties.animationFrameIndex;
|
||||||
elModelAnimationSettings.value = properties.animationSettings;
|
elModelAnimationSettings.value = properties.animationSettings;
|
||||||
elModelTextures.value = properties.textures;
|
elModelTextures.value = properties.textures;
|
||||||
elModelOriginalTextures.value = properties.originalTextures;
|
elModelOriginalTextures.value = properties.originalTextures;
|
||||||
} else if (properties.type == "Web") {
|
} else if (properties.type == "Web") {
|
||||||
for (var i = 0; i < elWebSections.length; i++) {
|
for (var i = 0; i < elWebSections.length; i++) {
|
||||||
elWebSections[i].style.display = 'block';
|
elWebSections[i].style.display = 'block';
|
||||||
|
@ -562,6 +574,20 @@
|
||||||
|
|
||||||
showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox');
|
showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox');
|
||||||
showElements(document.getElementsByClassName('atmosphere-section'), elZoneBackgroundMode.value == 'atmosphere');
|
showElements(document.getElementsByClassName('atmosphere-section'), elZoneBackgroundMode.value == 'atmosphere');
|
||||||
|
} else if (properties.type == "ParticleEffect") {
|
||||||
|
for (var i = 0; i < elParticleSections.length; i++) {
|
||||||
|
elParticleSections[i].style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
elParticleMaxParticles.value = properties.maxParticles;
|
||||||
|
elParticleLifeSpan.value = properties.lifespan.toFixed(2);
|
||||||
|
elParticleEmitRate.value = properties.emitRate.toFixed(1);
|
||||||
|
elParticleEmitDirectionX.value = properties.emitDirection.x.toFixed(2);
|
||||||
|
elParticleEmitDirectionY.value = properties.emitDirection.y.toFixed(2);
|
||||||
|
elParticleEmitDirectionZ.value = properties.emitDirection.z.toFixed(2);
|
||||||
|
elParticleEmitStrength.value = properties.emitStrength.toFixed(2);
|
||||||
|
elParticleLocalGravity.value = properties.localGravity.toFixed(2);
|
||||||
|
elParticleRadius.value = properties.particleRadius.toFixed(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
|
@ -678,6 +704,18 @@
|
||||||
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff'));
|
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff'));
|
||||||
|
|
||||||
elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
|
elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
|
||||||
|
|
||||||
|
elParticleMaxParticles.addEventListener('change', createEmitNumberPropertyUpdateFunction('maxParticles'));
|
||||||
|
elParticleLifeSpan.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifespan'));
|
||||||
|
elParticleEmitRate.addEventListener('change', createEmitNumberPropertyUpdateFunction('emitRate'));
|
||||||
|
var particleEmitDirectionChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier(
|
||||||
|
'emitDirection', elParticleEmitDirectionX, elParticleEmitDirectionY, elParticleEmitDirectionZ, DEGREES_TO_RADIANS);
|
||||||
|
elParticleEmitDirectionX.addEventListener('change', particleEmitDirectionChangeFunction);
|
||||||
|
elParticleEmitDirectionY.addEventListener('change', particleEmitDirectionChangeFunction);
|
||||||
|
elParticleEmitDirectionZ.addEventListener('change', particleEmitDirectionChangeFunction);
|
||||||
|
elParticleEmitStrength.addEventListener('change', createEmitNumberPropertyUpdateFunction('emitStrength'));
|
||||||
|
elParticleLocalGravity.addEventListener('change', createEmitNumberPropertyUpdateFunction('localGravity'));
|
||||||
|
elParticleRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('particleRadius'));
|
||||||
|
|
||||||
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
|
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
|
||||||
elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
|
elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
|
||||||
|
@ -1069,7 +1107,52 @@
|
||||||
<input type="text" id="property-web-source-url" class="url"></input>
|
<input type="text" id="property-web-source-url" class="url"></input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="particle-section property">
|
||||||
|
<div class="label">Max Particles</div>
|
||||||
|
<div class="value">
|
||||||
|
<input type='number' id="property-particle-maxparticles" min="0" max="2048" step="1"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="particle-section property">
|
||||||
|
<div class="label">Particle Life Span</div>
|
||||||
|
<div class="value">
|
||||||
|
<input type='number' id="property-particle-lifespan" min="0" step="0.1"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="particle-section property">
|
||||||
|
<div class="label">Particle Emission Rate</div>
|
||||||
|
<div class="value">
|
||||||
|
<input type='number' id="property-particle-emit-rate" min="0" step="0.5"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="particle-section property">
|
||||||
|
<div class="label">Particle Emission Direction</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="input-area">X <input class="coord" type='number' id="property-particle-emit-direction-x"></input></div>
|
||||||
|
<div class="input-area">Y <input class="coord" type='number' id="property-particle-emit-direction-y"></input></div>
|
||||||
|
<div class="input-area">Z <input class="coord" type='number' id="property-particle-emit-direction-z"></input></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="particle-section property">
|
||||||
|
<div class="label">Particle Emission Strength</div>
|
||||||
|
<div class="value">
|
||||||
|
<input type='number' id="property-particle-emit-strength" min="0" step="0.1"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="particle-section property">
|
||||||
|
<div class="label">Particle Local Gravity</div>
|
||||||
|
<div class="value">
|
||||||
|
<input class="coord" type='number' id="property-particle-localgravity" step="0.05"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="particle-section property">
|
||||||
|
<div class="label">Particle Radius</div>
|
||||||
|
<div class="value">
|
||||||
|
<input class="coord" type='number' id="property-particle-radius" min="0" step="0.005"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="model-section property">
|
<div class="model-section property">
|
||||||
<div class="label">Model URL</div>
|
<div class="label">Model URL</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
|
|
|
@ -693,6 +693,10 @@ Application::~Application() {
|
||||||
// stop the glWidget frame timer so it doesn't call paintGL
|
// stop the glWidget frame timer so it doesn't call paintGL
|
||||||
_glWidget->stopFrameTimer();
|
_glWidget->stopFrameTimer();
|
||||||
|
|
||||||
|
// remove avatars from physics engine
|
||||||
|
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||||
|
_physicsEngine.deleteObjects(DependencyManager::get<AvatarManager>()->getObjectsToDelete());
|
||||||
|
|
||||||
DependencyManager::destroy<OffscreenUi>();
|
DependencyManager::destroy<OffscreenUi>();
|
||||||
DependencyManager::destroy<AvatarManager>();
|
DependencyManager::destroy<AvatarManager>();
|
||||||
DependencyManager::destroy<AnimationCache>();
|
DependencyManager::destroy<AnimationCache>();
|
||||||
|
@ -2166,7 +2170,7 @@ void Application::init() {
|
||||||
_physicsEngine.init();
|
_physicsEngine.init();
|
||||||
|
|
||||||
EntityTree* tree = _entities.getTree();
|
EntityTree* tree = _entities.getTree();
|
||||||
_entitySimulation.init(tree, &_physicsEngine, &_shapeManager, &_entityEditSender);
|
_entitySimulation.init(tree, &_physicsEngine, &_entityEditSender);
|
||||||
tree->setSimulation(&_entitySimulation);
|
tree->setSimulation(&_entitySimulation);
|
||||||
|
|
||||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||||
|
@ -2486,12 +2490,21 @@ void Application::update(float deltaTime) {
|
||||||
_physicsEngine.changeObjects(_entitySimulation.getObjectsToChange());
|
_physicsEngine.changeObjects(_entitySimulation.getObjectsToChange());
|
||||||
_entitySimulation.unlock();
|
_entitySimulation.unlock();
|
||||||
|
|
||||||
|
AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
|
||||||
|
_physicsEngine.deleteObjects(avatarManager->getObjectsToDelete());
|
||||||
|
_physicsEngine.addObjects(avatarManager->getObjectsToAdd());
|
||||||
|
_physicsEngine.changeObjects(avatarManager->getObjectsToChange());
|
||||||
|
|
||||||
_physicsEngine.stepSimulation();
|
_physicsEngine.stepSimulation();
|
||||||
|
|
||||||
if (_physicsEngine.hasOutgoingChanges()) {
|
if (_physicsEngine.hasOutgoingChanges()) {
|
||||||
_entitySimulation.lock();
|
_entitySimulation.lock();
|
||||||
_entitySimulation.handleOutgoingChanges(_physicsEngine.getOutgoingChanges(), _physicsEngine.getSessionID());
|
_entitySimulation.handleOutgoingChanges(_physicsEngine.getOutgoingChanges(), _physicsEngine.getSessionID());
|
||||||
_entitySimulation.unlock();
|
_entitySimulation.unlock();
|
||||||
|
|
||||||
|
avatarManager->handleOutgoingChanges(_physicsEngine.getOutgoingChanges());
|
||||||
|
avatarManager->handleCollisionEvents(_physicsEngine.getCollisionEvents());
|
||||||
|
|
||||||
_physicsEngine.dumpStatsIfNecessary();
|
_physicsEngine.dumpStatsIfNecessary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
#include "AvatarManager.h"
|
#include "AvatarManager.h"
|
||||||
|
#include "AvatarMotionState.h"
|
||||||
#include "Hand.h"
|
#include "Hand.h"
|
||||||
#include "Head.h"
|
#include "Head.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
@ -969,6 +970,9 @@ int Avatar::parseDataAtOffset(const QByteArray& packet, int offset) {
|
||||||
|
|
||||||
const float MOVE_DISTANCE_THRESHOLD = 0.001f;
|
const float MOVE_DISTANCE_THRESHOLD = 0.001f;
|
||||||
_moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD;
|
_moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD;
|
||||||
|
if (_moving && _motionState) {
|
||||||
|
_motionState->addDirtyFlags(EntityItem::DIRTY_POSITION);
|
||||||
|
}
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
@ -1084,20 +1088,15 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void Avatar::rebuildSkeletonBody() {
|
void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||||
/* TODO: implement this and remove override from MyAvatar (when we have AvatarMotionStates working)
|
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
|
||||||
if (_motionState) {
|
shapeInfo.setCapsuleY(capsule.getRadius(), capsule.getHalfHeight());
|
||||||
// compute localAABox
|
shapeInfo.setOffset(_skeletonModel.getBoundingShapeOffset());
|
||||||
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
|
}
|
||||||
float radius = capsule.getRadius();
|
|
||||||
float height = 2.0f * (capsule.getHalfHeight() + radius);
|
// virtual
|
||||||
glm::vec3 corner(-radius, -0.5f * height, -radius);
|
void Avatar::rebuildSkeletonBody() {
|
||||||
corner += _skeletonModel.getBoundingShapeOffset();
|
DependencyManager::get<AvatarManager>()->updateAvatarPhysicsShape(getSessionUUID());
|
||||||
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
|
|
||||||
//_characterController.setLocalBoundingBox(corner, scale);
|
|
||||||
_motionState->setBoundingBox(corner, scale);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
#include <QtCore/QUuid>
|
#include <QtCore/QUuid>
|
||||||
|
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
|
#include <ShapeInfo.h>
|
||||||
|
|
||||||
#include "Hand.h"
|
#include "Hand.h"
|
||||||
#include "Head.h"
|
#include "Head.h"
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
#include "Recorder.h"
|
|
||||||
#include "SkeletonModel.h"
|
#include "SkeletonModel.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ enum ScreenTintLayer {
|
||||||
NUM_SCREEN_TINT_LAYERS
|
NUM_SCREEN_TINT_LAYERS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AvatarMotionState;
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
class Avatar : public AvatarData {
|
class Avatar : public AvatarData {
|
||||||
|
@ -146,9 +147,9 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec3 getNeckPosition() const;
|
Q_INVOKABLE glm::vec3 getNeckPosition() const;
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
|
Q_INVOKABLE const glm::vec3& getAcceleration() const { return _acceleration; }
|
||||||
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
Q_INVOKABLE const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
|
||||||
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
Q_INVOKABLE const glm::vec3& getAngularAcceleration() const { return _angularAcceleration; }
|
||||||
|
|
||||||
|
|
||||||
/// Scales a world space position vector relative to the avatar position and scale
|
/// Scales a world space position vector relative to the avatar position and scale
|
||||||
|
@ -164,6 +165,10 @@ public:
|
||||||
|
|
||||||
virtual void rebuildSkeletonBody();
|
virtual void rebuildSkeletonBody();
|
||||||
|
|
||||||
|
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
|
||||||
|
|
||||||
|
friend class AvatarManager;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
|
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
|
||||||
|
|
||||||
|
@ -231,7 +236,7 @@ private:
|
||||||
|
|
||||||
int _voiceSphereID;
|
int _voiceSphereID;
|
||||||
|
|
||||||
//AvatarMotionState* _motionState = nullptr;
|
AvatarMotionState* _motionState = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Avatar_h
|
#endif // hifi_Avatar_h
|
||||||
|
|
|
@ -92,22 +92,18 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
// simulate avatars
|
// simulate avatars
|
||||||
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||||
while (avatarIterator != _avatarHash.end()) {
|
while (avatarIterator != _avatarHash.end()) {
|
||||||
AvatarSharedPointer sharedAvatar = avatarIterator.value();
|
Avatar* avatar = reinterpret_cast<Avatar*>(avatarIterator.value().data());
|
||||||
Avatar* avatar = reinterpret_cast<Avatar*>(sharedAvatar.data());
|
|
||||||
|
|
||||||
if (sharedAvatar == _myAvatar || !avatar->isInitialized()) {
|
if (avatar == _myAvatar || !avatar->isInitialized()) {
|
||||||
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
||||||
// DO NOT update uninitialized Avatars
|
// DO NOT update or fade out uninitialized Avatars
|
||||||
++avatarIterator;
|
++avatarIterator;
|
||||||
continue;
|
} else if (avatar->shouldDie()) {
|
||||||
}
|
_avatarFades.push_back(avatarIterator.value());
|
||||||
if (!shouldKillAvatar(sharedAvatar)) {
|
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||||
// this avatar's mixer is still around, go ahead and simulate it
|
} else {
|
||||||
avatar->simulate(deltaTime);
|
avatar->simulate(deltaTime);
|
||||||
++avatarIterator;
|
++avatarIterator;
|
||||||
} else {
|
|
||||||
// the mixer that owned this avatar is gone, give it to the vector of fades and kill it
|
|
||||||
avatarIterator = erase(avatarIterator);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,24 +171,52 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
||||||
return AvatarSharedPointer(new Avatar());
|
return AvatarSharedPointer(new Avatar());
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) {
|
// virtual
|
||||||
if (iterator.key() != MY_AVATAR_KEY) {
|
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||||
if (reinterpret_cast<Avatar*>(iterator.value().data())->isInitialized()) {
|
AvatarSharedPointer avatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer);
|
||||||
_avatarFades.push_back(iterator.value());
|
return avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// protected
|
||||||
|
void AvatarManager::removeAvatarMotionState(Avatar* avatar) {
|
||||||
|
AvatarMotionState* motionState= avatar->_motionState;
|
||||||
|
if (motionState) {
|
||||||
|
// clean up physics stuff
|
||||||
|
motionState->clearObjectBackPointer();
|
||||||
|
avatar->_motionState = nullptr;
|
||||||
|
_avatarMotionStates.remove(motionState);
|
||||||
|
_motionStatesToAdd.remove(motionState);
|
||||||
|
_motionStatesToDelete.push_back(motionState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
||||||
|
AvatarHash::iterator avatarIterator = _avatarHash.find(sessionUUID);
|
||||||
|
if (avatarIterator != _avatarHash.end()) {
|
||||||
|
Avatar* avatar = reinterpret_cast<Avatar*>(avatarIterator.value().data());
|
||||||
|
if (avatar != _myAvatar && avatar->isInitialized()) {
|
||||||
|
removeAvatarMotionState(avatar);
|
||||||
|
|
||||||
|
_avatarFades.push_back(avatarIterator.value());
|
||||||
|
_avatarHash.erase(avatarIterator);
|
||||||
}
|
}
|
||||||
return AvatarHashMap::erase(iterator);
|
|
||||||
} else {
|
|
||||||
// never remove _myAvatar from the list
|
|
||||||
AvatarHash::iterator returnIterator = iterator;
|
|
||||||
return ++returnIterator;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::clearOtherAvatars() {
|
void AvatarManager::clearOtherAvatars() {
|
||||||
// clear any avatars that came from an avatar-mixer
|
// clear any avatars that came from an avatar-mixer
|
||||||
AvatarHash::iterator removeAvatar = _avatarHash.begin();
|
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||||
while (removeAvatar != _avatarHash.end()) {
|
while (avatarIterator != _avatarHash.end()) {
|
||||||
removeAvatar = erase(removeAvatar);
|
Avatar* avatar = reinterpret_cast<Avatar*>(avatarIterator.value().data());
|
||||||
|
if (avatar == _myAvatar || !avatar->isInitialized()) {
|
||||||
|
// don't remove myAvatar or uninitialized avatars from the list
|
||||||
|
++avatarIterator;
|
||||||
|
} else {
|
||||||
|
removeAvatarMotionState(avatar);
|
||||||
|
_avatarFades.push_back(avatarIterator.value());
|
||||||
|
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_myAvatar->clearLookAtTargetAvatar();
|
_myAvatar->clearLookAtTargetAvatar();
|
||||||
}
|
}
|
||||||
|
@ -215,3 +239,57 @@ QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const {
|
||||||
return _localLights;
|
return _localLights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VectorOfMotionStates& AvatarManager::getObjectsToDelete() {
|
||||||
|
_tempMotionStates.clear();
|
||||||
|
_tempMotionStates.swap(_motionStatesToDelete);
|
||||||
|
return _tempMotionStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorOfMotionStates& AvatarManager::getObjectsToAdd() {
|
||||||
|
_tempMotionStates.clear();
|
||||||
|
|
||||||
|
for (auto motionState : _motionStatesToAdd) {
|
||||||
|
_tempMotionStates.push_back(motionState);
|
||||||
|
}
|
||||||
|
_motionStatesToAdd.clear();
|
||||||
|
return _tempMotionStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorOfMotionStates& AvatarManager::getObjectsToChange() {
|
||||||
|
_tempMotionStates.clear();
|
||||||
|
for (auto state : _avatarMotionStates) {
|
||||||
|
if (state->_dirtyFlags > 0) {
|
||||||
|
_tempMotionStates.push_back(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _tempMotionStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarManager::handleOutgoingChanges(VectorOfMotionStates& motionStates) {
|
||||||
|
// TODO: extract the MyAvatar results once we use a MotionState for it.
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarManager::handleCollisionEvents(CollisionEvents& collisionEvents) {
|
||||||
|
// TODO: expose avatar collision events to JS
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) {
|
||||||
|
AvatarHash::iterator avatarItr = _avatarHash.find(id);
|
||||||
|
if (avatarItr != _avatarHash.end()) {
|
||||||
|
Avatar* avatar = static_cast<Avatar*>(avatarItr.value().data());
|
||||||
|
AvatarMotionState* motionState = avatar->_motionState;
|
||||||
|
if (motionState) {
|
||||||
|
motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE);
|
||||||
|
} else {
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
|
avatar->computeShapeInfo(shapeInfo);
|
||||||
|
btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||||
|
if (shape) {
|
||||||
|
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||||
|
avatar->_motionState = motionState;
|
||||||
|
_motionStatesToAdd.insert(motionState);
|
||||||
|
_avatarMotionStates.insert(motionState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
#include <QtCore/QSharedPointer>
|
#include <QtCore/QSharedPointer>
|
||||||
|
|
||||||
#include <AvatarHashMap.h>
|
#include <AvatarHashMap.h>
|
||||||
|
#include <PhysicsEngine.h>
|
||||||
|
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
|
#include "AvatarMotionState.h"
|
||||||
|
|
||||||
class MyAvatar;
|
class MyAvatar;
|
||||||
|
|
||||||
|
@ -51,6 +53,14 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
|
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
|
||||||
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
|
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
|
||||||
|
|
||||||
|
VectorOfMotionStates& getObjectsToDelete();
|
||||||
|
VectorOfMotionStates& getObjectsToAdd();
|
||||||
|
VectorOfMotionStates& getObjectsToChange();
|
||||||
|
void handleOutgoingChanges(VectorOfMotionStates& motionStates);
|
||||||
|
void handleCollisionEvents(CollisionEvents& collisionEvents);
|
||||||
|
|
||||||
|
void updateAvatarPhysicsShape(const QUuid& id);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
||||||
|
@ -62,10 +72,11 @@ private:
|
||||||
void simulateAvatarFades(float deltaTime);
|
void simulateAvatarFades(float deltaTime);
|
||||||
void renderAvatarFades(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
|
void renderAvatarFades(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
|
||||||
|
|
||||||
AvatarSharedPointer newSharedAvatar();
|
|
||||||
|
|
||||||
// virtual overrides
|
// virtual overrides
|
||||||
AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
|
virtual AvatarSharedPointer newSharedAvatar();
|
||||||
|
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
|
void removeAvatarMotionState(Avatar* avatar);
|
||||||
|
virtual void removeAvatar(const QUuid& sessionUUID);
|
||||||
|
|
||||||
QVector<AvatarSharedPointer> _avatarFades;
|
QVector<AvatarSharedPointer> _avatarFades;
|
||||||
QSharedPointer<MyAvatar> _myAvatar;
|
QSharedPointer<MyAvatar> _myAvatar;
|
||||||
|
@ -74,6 +85,11 @@ private:
|
||||||
QVector<AvatarManager::LocalLight> _localLights;
|
QVector<AvatarManager::LocalLight> _localLights;
|
||||||
|
|
||||||
bool _shouldShowReceiveStats = false;
|
bool _shouldShowReceiveStats = false;
|
||||||
|
|
||||||
|
SetOfAvatarMotionStates _avatarMotionStates;
|
||||||
|
SetOfMotionStates _motionStatesToAdd;
|
||||||
|
VectorOfMotionStates _motionStatesToDelete;
|
||||||
|
VectorOfMotionStates _tempMotionStates;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
||||||
|
|
160
interface/src/avatar/AvatarMotionState.cpp
Normal file
160
interface/src/avatar/AvatarMotionState.cpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
//
|
||||||
|
// AvatarMotionState.cpp
|
||||||
|
// interface/src/avatar/
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2015.05.14
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <PhysicsHelpers.h>
|
||||||
|
|
||||||
|
#include "Avatar.h"
|
||||||
|
#include "AvatarMotionState.h"
|
||||||
|
#include "BulletUtil.h"
|
||||||
|
|
||||||
|
AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||||
|
assert(_avatar);
|
||||||
|
if (_shape) {
|
||||||
|
_mass = 100.0f; // HACK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarMotionState::~AvatarMotionState() {
|
||||||
|
_avatar = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
uint32_t AvatarMotionState::getAndClearIncomingDirtyFlags() {
|
||||||
|
uint32_t dirtyFlags = 0;
|
||||||
|
if (_body && _avatar) {
|
||||||
|
dirtyFlags = _dirtyFlags;
|
||||||
|
_dirtyFlags = 0;
|
||||||
|
}
|
||||||
|
return dirtyFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionType AvatarMotionState::computeObjectMotionType() const {
|
||||||
|
// TODO?: support non-DYNAMIC motion for avatars? (e.g. when sitting)
|
||||||
|
return MOTION_TYPE_DYNAMIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual and protected
|
||||||
|
btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||||
|
if (_avatar) {
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
|
_avatar->computeShapeInfo(shapeInfo);
|
||||||
|
return getShapeManager()->getShape(shapeInfo);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
bool AvatarMotionState::isMoving() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||||
|
if (!_avatar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
worldTrans.setOrigin(glmToBullet(getObjectPosition()));
|
||||||
|
worldTrans.setRotation(glmToBullet(getObjectRotation()));
|
||||||
|
if (_body) {
|
||||||
|
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity()));
|
||||||
|
_body->setAngularVelocity(glmToBullet(getObjectLinearVelocity()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
|
if (!_avatar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// HACK: The PhysicsEngine does not actually move OTHER avatars -- instead it slaves their local RigidBody to the transform
|
||||||
|
// as specified by a remote simulation. However, to give the remote simulation time to respond to our own objects we tie
|
||||||
|
// the other avatar's body to its true position with a simple spring. This is a HACK that will have to be improved later.
|
||||||
|
const float SPRING_TIMESCALE = 0.5f;
|
||||||
|
float tau = PHYSICS_ENGINE_FIXED_SUBSTEP / SPRING_TIMESCALE;
|
||||||
|
btVector3 currentPosition = worldTrans.getOrigin();
|
||||||
|
btVector3 targetPosition = glmToBullet(getObjectPosition());
|
||||||
|
btTransform newTransform;
|
||||||
|
newTransform.setOrigin((1.0f - tau) * currentPosition + tau * targetPosition);
|
||||||
|
newTransform.setRotation(glmToBullet(getObjectRotation()));
|
||||||
|
_body->setWorldTransform(newTransform);
|
||||||
|
_body->setLinearVelocity(glmToBullet(getObjectLinearVelocity()));
|
||||||
|
_body->setAngularVelocity(glmToBullet(getObjectLinearVelocity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// These pure virtual methods must be implemented for each MotionState type
|
||||||
|
// and make it possible to implement more complicated methods in this base class.
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
float AvatarMotionState::getObjectRestitution() const {
|
||||||
|
return 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
float AvatarMotionState::getObjectFriction() const {
|
||||||
|
return 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
float AvatarMotionState::getObjectLinearDamping() const {
|
||||||
|
return 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
float AvatarMotionState::getObjectAngularDamping() const {
|
||||||
|
return 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
glm::vec3 AvatarMotionState::getObjectPosition() const {
|
||||||
|
return _avatar->getPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
glm::quat AvatarMotionState::getObjectRotation() const {
|
||||||
|
return _avatar->getOrientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
const glm::vec3& AvatarMotionState::getObjectLinearVelocity() const {
|
||||||
|
return _avatar->getVelocity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
const glm::vec3& AvatarMotionState::getObjectAngularVelocity() const {
|
||||||
|
return _avatar->getAngularVelocity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
const glm::vec3& AvatarMotionState::getObjectGravity() const {
|
||||||
|
return _avatar->getAcceleration();
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
const QUuid& AvatarMotionState::getObjectID() const {
|
||||||
|
return _avatar->getSessionUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
QUuid AvatarMotionState::getSimulatorID() const {
|
||||||
|
return _avatar->getSessionUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
void AvatarMotionState::bump() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
void AvatarMotionState::clearObjectBackPointer() {
|
||||||
|
ObjectMotionState::clearObjectBackPointer();
|
||||||
|
_avatar = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
75
interface/src/avatar/AvatarMotionState.h
Normal file
75
interface/src/avatar/AvatarMotionState.h
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
//
|
||||||
|
// AvatarMotionState.h
|
||||||
|
// interface/src/avatar/
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2015.05.14
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AvatarMotionState_h
|
||||||
|
#define hifi_AvatarMotionState_h
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
|
#include <ObjectMotionState.h>
|
||||||
|
|
||||||
|
class Avatar;
|
||||||
|
|
||||||
|
class AvatarMotionState : public ObjectMotionState {
|
||||||
|
public:
|
||||||
|
AvatarMotionState(Avatar* avatar, btCollisionShape* shape);
|
||||||
|
~AvatarMotionState();
|
||||||
|
|
||||||
|
virtual MotionType getMotionType() const { return _motionType; }
|
||||||
|
|
||||||
|
virtual uint32_t getAndClearIncomingDirtyFlags();
|
||||||
|
|
||||||
|
virtual MotionType computeObjectMotionType() const;
|
||||||
|
|
||||||
|
virtual bool isMoving() const;
|
||||||
|
|
||||||
|
// this relays incoming position/rotation to the RigidBody
|
||||||
|
virtual void getWorldTransform(btTransform& worldTrans) const;
|
||||||
|
|
||||||
|
// this relays outgoing position/rotation to the EntityItem
|
||||||
|
virtual void setWorldTransform(const btTransform& worldTrans);
|
||||||
|
|
||||||
|
|
||||||
|
// These pure virtual methods must be implemented for each MotionState type
|
||||||
|
// and make it possible to implement more complicated methods in this base class.
|
||||||
|
|
||||||
|
virtual float getObjectRestitution() const;
|
||||||
|
virtual float getObjectFriction() const;
|
||||||
|
virtual float getObjectLinearDamping() const;
|
||||||
|
virtual float getObjectAngularDamping() const;
|
||||||
|
|
||||||
|
virtual glm::vec3 getObjectPosition() const;
|
||||||
|
virtual glm::quat getObjectRotation() const;
|
||||||
|
virtual const glm::vec3& getObjectLinearVelocity() const;
|
||||||
|
virtual const glm::vec3& getObjectAngularVelocity() const;
|
||||||
|
virtual const glm::vec3& getObjectGravity() const;
|
||||||
|
|
||||||
|
virtual const QUuid& getObjectID() const;
|
||||||
|
|
||||||
|
virtual QUuid getSimulatorID() const;
|
||||||
|
virtual void bump();
|
||||||
|
|
||||||
|
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);
|
||||||
|
|
||||||
|
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
|
||||||
|
|
||||||
|
friend class AvatarManager;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual btCollisionShape* computeNewShape();
|
||||||
|
virtual void clearObjectBackPointer();
|
||||||
|
Avatar* _avatar;
|
||||||
|
uint32_t _dirtyFlags;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QSet<AvatarMotionState*> SetOfAvatarMotionStates;
|
||||||
|
|
||||||
|
#endif // hifi_AvatarMotionState_h
|
|
@ -41,6 +41,7 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
||||||
_headClipDistance(DEFAULT_NEAR_CLIP)
|
_headClipDistance(DEFAULT_NEAR_CLIP)
|
||||||
{
|
{
|
||||||
assert(_owningAvatar);
|
assert(_owningAvatar);
|
||||||
|
_enableShapes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SkeletonModel::~SkeletonModel() {
|
SkeletonModel::~SkeletonModel() {
|
||||||
|
|
|
@ -69,6 +69,7 @@ const quint32 AVATAR_MOTION_DEFAULTS =
|
||||||
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
|
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
|
||||||
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
|
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
|
||||||
|
|
||||||
|
const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND;
|
||||||
|
|
||||||
// Bitset of state flags - we store the key state, hand state, faceshift, chat circling, and existance of
|
// Bitset of state flags - we store the key state, hand state, faceshift, chat circling, and existance of
|
||||||
// referential data in this bit set. The hand state is an octal, but is split into two sections to maintain
|
// referential data in this bit set. The hand state is an octal, but is split into two sections to maintain
|
||||||
|
@ -290,7 +291,6 @@ public:
|
||||||
QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); }
|
QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); }
|
||||||
void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); }
|
void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); }
|
||||||
|
|
||||||
Node* getOwningAvatarMixer() { return _owningAvatarMixer.data(); }
|
|
||||||
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
|
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
|
||||||
|
|
||||||
const AABox& getLocalAABox() const { return _localAABox; }
|
const AABox& getLocalAABox() const { return _localAABox; }
|
||||||
|
@ -301,8 +301,10 @@ public:
|
||||||
int getReceiveRate() const;
|
int getReceiveRate() const;
|
||||||
|
|
||||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
||||||
Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
|
Q_INVOKABLE const glm::vec3& getVelocity() const { return _velocity; }
|
||||||
glm::vec3 getTargetVelocity() const { return _targetVelocity; }
|
const glm::vec3& getTargetVelocity() const { return _targetVelocity; }
|
||||||
|
|
||||||
|
bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void sendAvatarDataPacket();
|
void sendAvatarDataPacket();
|
||||||
|
|
|
@ -20,19 +20,6 @@ AvatarHashMap::AvatarHashMap() {
|
||||||
connect(DependencyManager::get<NodeList>().data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged);
|
connect(DependencyManager::get<NodeList>().data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AvatarHash::iterator AvatarHashMap::erase(const AvatarHash::iterator& iterator) {
|
|
||||||
qCDebug(avatars) << "Removing Avatar with UUID" << iterator.key() << "from AvatarHashMap.";
|
|
||||||
return _avatarHash.erase(iterator);
|
|
||||||
}
|
|
||||||
|
|
||||||
const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND;
|
|
||||||
|
|
||||||
bool AvatarHashMap::shouldKillAvatar(const AvatarSharedPointer& sharedAvatar) {
|
|
||||||
return (sharedAvatar->getOwningAvatarMixer() == NULL
|
|
||||||
|| sharedAvatar->getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AvatarHashMap::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) {
|
void AvatarHashMap::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||||
switch (packetTypeForPacket(datagram)) {
|
switch (packetTypeForPacket(datagram)) {
|
||||||
case PacketTypeBulkAvatarData:
|
case PacketTypeBulkAvatarData:
|
||||||
|
@ -52,10 +39,6 @@ void AvatarHashMap::processAvatarMixerDatagram(const QByteArray& datagram, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvatarHashMap::containsAvatarWithDisplayName(const QString& displayName) {
|
|
||||||
return !avatarWithDisplayName(displayName).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) {
|
bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) {
|
||||||
foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) {
|
foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) {
|
||||||
glm::vec3 avatarPosition = sharedAvatar->getPosition();
|
glm::vec3 avatarPosition = sharedAvatar->getPosition();
|
||||||
|
@ -67,45 +50,19 @@ bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarWeakPointer AvatarHashMap::avatarWithDisplayName(const QString& displayName) {
|
|
||||||
foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) {
|
|
||||||
if (sharedAvatar->getDisplayName() == displayName) {
|
|
||||||
// this is a match
|
|
||||||
// check if this avatar should still be around
|
|
||||||
if (!shouldKillAvatar(sharedAvatar)) {
|
|
||||||
// we have a match, return the AvatarData
|
|
||||||
return sharedAvatar;
|
|
||||||
} else {
|
|
||||||
// we should remove this avatar, but we might not be on a thread that is allowed
|
|
||||||
// so we just return NULL to the caller
|
|
||||||
return AvatarWeakPointer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AvatarWeakPointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
AvatarSharedPointer AvatarHashMap::newSharedAvatar() {
|
AvatarSharedPointer AvatarHashMap::newSharedAvatar() {
|
||||||
return AvatarSharedPointer(new AvatarData());
|
return AvatarSharedPointer(new AvatarData());
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarSharedPointer AvatarHashMap::matchingOrNewAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||||
AvatarSharedPointer matchingAvatar = _avatarHash.value(sessionUUID);
|
qCDebug(avatars) << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap.";
|
||||||
|
|
||||||
if (!matchingAvatar) {
|
AvatarSharedPointer avatar = newSharedAvatar();
|
||||||
// insert the new avatar into our hash
|
avatar->setSessionUUID(sessionUUID);
|
||||||
matchingAvatar = newSharedAvatar();
|
avatar->setOwningAvatarMixer(mixerWeakPointer);
|
||||||
|
_avatarHash.insert(sessionUUID, avatar);
|
||||||
qCDebug(avatars) << "Adding avatar with sessionUUID " << sessionUUID << "to AvatarHashMap.";
|
|
||||||
|
return avatar;
|
||||||
matchingAvatar->setSessionUUID(sessionUUID);
|
|
||||||
matchingAvatar->setOwningAvatarMixer(mixerWeakPointer);
|
|
||||||
|
|
||||||
_avatarHash.insert(sessionUUID, matchingAvatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchingAvatar;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarHashMap::processAvatarDataPacket(const QByteArray &datagram, const QWeakPointer<Node> &mixerWeakPointer) {
|
void AvatarHashMap::processAvatarDataPacket(const QByteArray &datagram, const QWeakPointer<Node> &mixerWeakPointer) {
|
||||||
|
@ -118,10 +75,13 @@ void AvatarHashMap::processAvatarDataPacket(const QByteArray &datagram, const QW
|
||||||
bytesRead += NUM_BYTES_RFC4122_UUID;
|
bytesRead += NUM_BYTES_RFC4122_UUID;
|
||||||
|
|
||||||
if (sessionUUID != _lastOwnerSessionUUID) {
|
if (sessionUUID != _lastOwnerSessionUUID) {
|
||||||
AvatarSharedPointer matchingAvatarData = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
AvatarSharedPointer avatar = _avatarHash.value(sessionUUID);
|
||||||
|
if (!avatar) {
|
||||||
|
avatar = addAvatar(sessionUUID, mixerWeakPointer);
|
||||||
|
}
|
||||||
|
|
||||||
// have the matching (or new) avatar parse the data from the packet
|
// have the matching (or new) avatar parse the data from the packet
|
||||||
bytesRead += matchingAvatarData->parseDataAtOffset(datagram, bytesRead);
|
bytesRead += avatar->parseDataAtOffset(datagram, bytesRead);
|
||||||
} else {
|
} else {
|
||||||
// create a dummy AvatarData class to throw this data on the ground
|
// create a dummy AvatarData class to throw this data on the ground
|
||||||
AvatarData dummyData;
|
AvatarData dummyData;
|
||||||
|
@ -145,24 +105,24 @@ void AvatarHashMap::processAvatarIdentityPacket(const QByteArray &packet, const
|
||||||
identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName;
|
identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName;
|
||||||
|
|
||||||
// mesh URL for a UUID, find avatar in our list
|
// mesh URL for a UUID, find avatar in our list
|
||||||
AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
AvatarSharedPointer avatar = _avatarHash.value(sessionUUID);
|
||||||
if (matchingAvatar) {
|
if (!avatar) {
|
||||||
|
avatar = addAvatar(sessionUUID, mixerWeakPointer);
|
||||||
if (matchingAvatar->getFaceModelURL() != faceMeshURL) {
|
}
|
||||||
matchingAvatar->setFaceModelURL(faceMeshURL);
|
if (avatar->getFaceModelURL() != faceMeshURL) {
|
||||||
}
|
avatar->setFaceModelURL(faceMeshURL);
|
||||||
|
}
|
||||||
if (matchingAvatar->getSkeletonModelURL() != skeletonURL) {
|
|
||||||
matchingAvatar->setSkeletonModelURL(skeletonURL);
|
if (avatar->getSkeletonModelURL() != skeletonURL) {
|
||||||
}
|
avatar->setSkeletonModelURL(skeletonURL);
|
||||||
|
}
|
||||||
if (matchingAvatar->getAttachmentData() != attachmentData) {
|
|
||||||
matchingAvatar->setAttachmentData(attachmentData);
|
if (avatar->getAttachmentData() != attachmentData) {
|
||||||
}
|
avatar->setAttachmentData(attachmentData);
|
||||||
|
}
|
||||||
if (matchingAvatar->getDisplayName() != displayName) {
|
|
||||||
matchingAvatar->setDisplayName(displayName);
|
if (avatar->getDisplayName() != displayName) {
|
||||||
}
|
avatar->setDisplayName(displayName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,24 +131,25 @@ void AvatarHashMap::processAvatarBillboardPacket(const QByteArray& packet, const
|
||||||
int headerSize = numBytesForPacketHeader(packet);
|
int headerSize = numBytesForPacketHeader(packet);
|
||||||
QUuid sessionUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID));
|
QUuid sessionUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(sessionUUID, mixerWeakPointer);
|
AvatarSharedPointer avatar = _avatarHash.value(sessionUUID);
|
||||||
if (matchingAvatar) {
|
if (!avatar) {
|
||||||
QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID);
|
avatar = addAvatar(sessionUUID, mixerWeakPointer);
|
||||||
if (matchingAvatar->getBillboard() != billboard) {
|
}
|
||||||
matchingAvatar->setBillboard(billboard);
|
|
||||||
}
|
QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID);
|
||||||
|
if (avatar->getBillboard() != billboard) {
|
||||||
|
avatar->setBillboard(billboard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarHashMap::processKillAvatar(const QByteArray& datagram) {
|
void AvatarHashMap::processKillAvatar(const QByteArray& datagram) {
|
||||||
// read the node id
|
// read the node id
|
||||||
QUuid sessionUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(datagram), NUM_BYTES_RFC4122_UUID));
|
QUuid sessionUUID = QUuid::fromRfc4122(datagram.mid(numBytesForPacketHeader(datagram), NUM_BYTES_RFC4122_UUID));
|
||||||
|
removeAvatar(sessionUUID);
|
||||||
// remove the avatar with that UUID from our hash, if it exists
|
}
|
||||||
AvatarHash::iterator matchedAvatar = _avatarHash.find(sessionUUID);
|
|
||||||
if (matchedAvatar != _avatarHash.end()) {
|
void AvatarHashMap::removeAvatar(const QUuid& sessionUUID) {
|
||||||
erase(matchedAvatar);
|
_avatarHash.remove(sessionUUID);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID) {
|
void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID) {
|
||||||
|
|
|
@ -36,28 +36,26 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer);
|
void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
bool containsAvatarWithDisplayName(const QString& displayName);
|
|
||||||
bool isAvatarInRange(const glm::vec3 & position, const float range);
|
bool isAvatarInRange(const glm::vec3 & position, const float range);
|
||||||
AvatarWeakPointer avatarWithDisplayName(const QString& displayname);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID);
|
void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AvatarHashMap();
|
AvatarHashMap();
|
||||||
virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator);
|
|
||||||
|
|
||||||
bool shouldKillAvatar(const AvatarSharedPointer& sharedAvatar);
|
|
||||||
|
|
||||||
virtual AvatarSharedPointer newSharedAvatar();
|
virtual AvatarSharedPointer newSharedAvatar();
|
||||||
AvatarSharedPointer matchingOrNewAvatar(const QUuid& nodeUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
|
virtual void removeAvatar(const QUuid& sessionUUID);
|
||||||
|
|
||||||
|
AvatarHash _avatarHash;
|
||||||
|
|
||||||
|
private:
|
||||||
void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
void processAvatarIdentityPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
void processAvatarIdentityPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
void processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
void processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
|
||||||
void processKillAvatar(const QByteArray& datagram);
|
void processKillAvatar(const QByteArray& datagram);
|
||||||
|
|
||||||
AvatarHash _avatarHash;
|
|
||||||
QUuid _lastOwnerSessionUUID;
|
QUuid _lastOwnerSessionUUID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -537,7 +537,7 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer en
|
||||||
|
|
||||||
if (entityItem->getType() == EntityTypes::Model) {
|
if (entityItem->getType() == EntityTypes::Model) {
|
||||||
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
||||||
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
||||||
assert(modelEntityItem); // we need this!!!
|
assert(modelEntityItem); // we need this!!!
|
||||||
Model* model = modelEntityItem->getModel(this);
|
Model* model = modelEntityItem->getModel(this);
|
||||||
if (model) {
|
if (model) {
|
||||||
|
@ -551,7 +551,7 @@ const Model* EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityI
|
||||||
const Model* result = NULL;
|
const Model* result = NULL;
|
||||||
if (entityItem->getType() == EntityTypes::Model) {
|
if (entityItem->getType() == EntityTypes::Model) {
|
||||||
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
||||||
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
||||||
result = modelEntityItem->getModel(this);
|
result = modelEntityItem->getModel(this);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -562,7 +562,7 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemP
|
||||||
|
|
||||||
if (entityItem->getType() == EntityTypes::Model) {
|
if (entityItem->getType() == EntityTypes::Model) {
|
||||||
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
||||||
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
||||||
if (modelEntityItem->hasCompoundShapeURL()) {
|
if (modelEntityItem->hasCompoundShapeURL()) {
|
||||||
Model* model = modelEntityItem->getModel(this);
|
Model* model = modelEntityItem->getModel(this);
|
||||||
if (model) {
|
if (model) {
|
||||||
|
|
|
@ -173,7 +173,6 @@ private:
|
||||||
|
|
||||||
bool _hasPreviousZone = false;
|
bool _hasPreviousZone = false;
|
||||||
std::shared_ptr<ZoneEntityItem> _bestZone;
|
std::shared_ptr<ZoneEntityItem> _bestZone;
|
||||||
|
|
||||||
float _bestZoneVolume;
|
float _bestZoneVolume;
|
||||||
|
|
||||||
glm::vec3 _previousKeyLightColor;
|
glm::vec3 _previousKeyLightColor;
|
||||||
|
|
|
@ -29,7 +29,6 @@ void RenderableLineEntityItem::render(RenderArgs* args) {
|
||||||
glm::vec3 p1 = ENTITY_ITEM_ZERO_VEC3;
|
glm::vec3 p1 = ENTITY_ITEM_ZERO_VEC3;
|
||||||
glm::vec3 p2 = getDimensions();
|
glm::vec3 p2 = getDimensions();
|
||||||
glm::vec4 lineColor(toGlm(getXColor()), getLocalRenderAlpha());
|
glm::vec4 lineColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||||
|
|
||||||
Q_ASSERT(args->_batch);
|
Q_ASSERT(args->_batch);
|
||||||
gpu::Batch& batch = *args->_batch;
|
gpu::Batch& batch = *args->_batch;
|
||||||
batch.setModelTransform(getTransformToCenter());
|
batch.setModelTransform(getTransformToCenter());
|
||||||
|
|
|
@ -43,18 +43,18 @@ void Mesh::addAttribute(Slot slot, const BufferView& buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::evalVertexFormat() {
|
void Mesh::evalVertexFormat() {
|
||||||
VertexFormat vf;
|
auto vf = new VertexFormat();
|
||||||
int channelNum = 0;
|
int channelNum = 0;
|
||||||
if (hasVertexData()) {
|
if (hasVertexData()) {
|
||||||
vf.setAttribute(gpu::Stream::POSITION, channelNum, _vertexBuffer._element, 0);
|
vf->setAttribute(gpu::Stream::POSITION, channelNum, _vertexBuffer._element, 0);
|
||||||
channelNum++;
|
channelNum++;
|
||||||
}
|
}
|
||||||
for (auto attrib : _attributeBuffers) {
|
for (auto attrib : _attributeBuffers) {
|
||||||
vf.setAttribute(attrib.first, channelNum, attrib.second._element, 0);
|
vf->setAttribute(attrib.first, channelNum, attrib.second._element, 0);
|
||||||
channelNum++;
|
channelNum++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_vertexFormat = vf;
|
_vertexFormat.reset(vf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::setIndexBuffer(const BufferView& buffer) {
|
void Mesh::setIndexBuffer(const BufferView& buffer) {
|
||||||
|
@ -112,12 +112,12 @@ const gpu::BufferStream Mesh::makeBufferStream() const {
|
||||||
|
|
||||||
int channelNum = 0;
|
int channelNum = 0;
|
||||||
if (hasVertexData()) {
|
if (hasVertexData()) {
|
||||||
stream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat.getChannelStride(channelNum));
|
stream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat->getChannelStride(channelNum));
|
||||||
channelNum++;
|
channelNum++;
|
||||||
}
|
}
|
||||||
for (auto attrib : _attributeBuffers) {
|
for (auto attrib : _attributeBuffers) {
|
||||||
BufferView& view = attrib.second;
|
BufferView& view = attrib.second;
|
||||||
stream.addBuffer(view._buffer, view._offset, _vertexFormat.getChannelStride(channelNum));
|
stream.addBuffer(view._buffer, view._offset, _vertexFormat->getChannelStride(channelNum));
|
||||||
channelNum++;
|
channelNum++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,14 +47,14 @@ public:
|
||||||
void setVertexBuffer(const BufferView& buffer);
|
void setVertexBuffer(const BufferView& buffer);
|
||||||
const BufferView& getVertexBuffer() const { return _vertexBuffer; }
|
const BufferView& getVertexBuffer() const { return _vertexBuffer; }
|
||||||
uint getNumVertices() const { return _vertexBuffer.getNumElements(); }
|
uint getNumVertices() const { return _vertexBuffer.getNumElements(); }
|
||||||
bool hasVertexData() const { return !_vertexBuffer._buffer; }
|
bool hasVertexData() const { return _vertexBuffer._buffer.get() != nullptr; }
|
||||||
|
|
||||||
// Attribute Buffers
|
// Attribute Buffers
|
||||||
int getNumAttributes() const { return _attributeBuffers.size(); }
|
int getNumAttributes() const { return _attributeBuffers.size(); }
|
||||||
void addAttribute(Slot slot, const BufferView& buffer);
|
void addAttribute(Slot slot, const BufferView& buffer);
|
||||||
|
|
||||||
// Stream format
|
// Stream format
|
||||||
const VertexFormat& getVertexFormat() const { return _vertexFormat; }
|
const gpu::Stream::FormatPointer getVertexFormat() const { return _vertexFormat; }
|
||||||
|
|
||||||
// Index Buffer
|
// Index Buffer
|
||||||
void setIndexBuffer(const BufferView& buffer);
|
void setIndexBuffer(const BufferView& buffer);
|
||||||
|
@ -114,7 +114,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
VertexFormat _vertexFormat;
|
gpu::Stream::FormatPointer _vertexFormat;
|
||||||
|
|
||||||
BufferView _vertexBuffer;
|
BufferView _vertexBuffer;
|
||||||
BufferViewMap _attributeBuffers;
|
BufferViewMap _attributeBuffers;
|
||||||
|
|
|
@ -27,7 +27,7 @@ void ReceivedPacketProcessor::queueReceivedPacket(const SharedNodePointer& sendi
|
||||||
_nodePacketCounts[sendingNode->getUUID()]++;
|
_nodePacketCounts[sendingNode->getUUID()]++;
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
// Make sure to wake our actual processing thread because we now have packets for it to process.
|
// Make sure to wake our actual processing thread because we now have packets for it to process.
|
||||||
_hasPackets.wakeAll();
|
_hasPackets.wakeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,9 @@ const float VIEW_FRUSTUM_FOV_OVERSEND = 60.0f;
|
||||||
// These are guards to prevent our voxel tree recursive routines from spinning out of control
|
// These are guards to prevent our voxel tree recursive routines from spinning out of control
|
||||||
const int UNREASONABLY_DEEP_RECURSION = 29; // use this for something that you want to be shallow, but not spin out
|
const int UNREASONABLY_DEEP_RECURSION = 29; // use this for something that you want to be shallow, but not spin out
|
||||||
const int DANGEROUSLY_DEEP_RECURSION = 200; // use this for something that needs to go deeper
|
const int DANGEROUSLY_DEEP_RECURSION = 200; // use this for something that needs to go deeper
|
||||||
const float SCALE_AT_UNREASONABLY_DEEP_RECURSION = (1.0f / powf(2.0f, UNREASONABLY_DEEP_RECURSION));
|
const float SCALE_AT_UNREASONABLY_DEEP_RECURSION = (TREE_SCALE / powf(2.0f, UNREASONABLY_DEEP_RECURSION));
|
||||||
const float SCALE_AT_DANGEROUSLY_DEEP_RECURSION = (1.0f / powf(2.0f, DANGEROUSLY_DEEP_RECURSION));
|
const float SCALE_AT_DANGEROUSLY_DEEP_RECURSION = (TREE_SCALE / powf(2.0f, DANGEROUSLY_DEEP_RECURSION));
|
||||||
const float SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE = SCALE_AT_UNREASONABLY_DEEP_RECURSION * 2.0f; // 0.00006103515 meter ~1/10,0000th
|
const float SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE = SCALE_AT_UNREASONABLY_DEEP_RECURSION * 2.0f; // 0.00001525878 meter ~1/10,0000th
|
||||||
|
|
||||||
const int DEFAULT_MAX_OCTREE_PPS = 600; // the default maximum PPS we think any octree based server should send to a client
|
const int DEFAULT_MAX_OCTREE_PPS = 600; // the default maximum PPS we think any octree based server should send to a client
|
||||||
|
|
||||||
|
|
|
@ -311,9 +311,11 @@ void DynamicCharacterController::updateShapeIfNecessary() {
|
||||||
// create new shape
|
// create new shape
|
||||||
_shape = new btCapsuleShape(_radius, 2.0f * _halfHeight);
|
_shape = new btCapsuleShape(_radius, 2.0f * _halfHeight);
|
||||||
|
|
||||||
|
// HACK: use some simple mass property defaults for now
|
||||||
|
float mass = 100.0f;
|
||||||
|
btVector3 inertia(30.0f, 8.0f, 30.0f);
|
||||||
|
|
||||||
// create new body
|
// create new body
|
||||||
float mass = 1.0f;
|
|
||||||
btVector3 inertia(1.0f, 1.0f, 1.0f);
|
|
||||||
_rigidBody = new btRigidBody(mass, nullptr, _shape, inertia);
|
_rigidBody = new btRigidBody(mass, nullptr, _shape, inertia);
|
||||||
_rigidBody->setSleepingThresholds(0.0f, 0.0f);
|
_rigidBody->setSleepingThresholds(0.0f, 0.0f);
|
||||||
_rigidBody->setAngularFactor(0.0f);
|
_rigidBody->setAngularFactor(0.0f);
|
||||||
|
|
|
@ -39,8 +39,9 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
||||||
_loopsSinceOwnershipBid(0),
|
_loopsSinceOwnershipBid(0),
|
||||||
_loopsWithoutOwner(0)
|
_loopsWithoutOwner(0)
|
||||||
{
|
{
|
||||||
_type = MOTION_STATE_TYPE_ENTITY;
|
_type = MOTIONSTATE_TYPE_ENTITY;
|
||||||
assert(entity != nullptr);
|
assert(_entity != nullptr);
|
||||||
|
setMass(_entity->computeMass());
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityMotionState::~EntityMotionState() {
|
EntityMotionState::~EntityMotionState() {
|
||||||
|
@ -88,7 +89,6 @@ void EntityMotionState::handleEasyChanges(uint32_t flags) {
|
||||||
if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) {
|
if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) {
|
||||||
_body->activate();
|
_body->activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,10 +98,9 @@ void EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine*
|
||||||
ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::clearEntity() {
|
void EntityMotionState::clearObjectBackPointer() {
|
||||||
|
ObjectMotionState::clearObjectBackPointer();
|
||||||
_entity = nullptr;
|
_entity = nullptr;
|
||||||
// set the type to INVALID so that external logic that pivots on the type won't try to access _entity
|
|
||||||
_type = MOTION_STATE_TYPE_INVALID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MotionType EntityMotionState::computeObjectMotionType() const {
|
MotionType EntityMotionState::computeObjectMotionType() const {
|
||||||
|
@ -178,10 +177,14 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::computeObjectShapeInfo(ShapeInfo& shapeInfo) {
|
// virtual and protected
|
||||||
|
btCollisionShape* EntityMotionState::computeNewShape() {
|
||||||
if (_entity) {
|
if (_entity) {
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
_entity->computeShapeInfo(shapeInfo);
|
_entity->computeShapeInfo(shapeInfo);
|
||||||
|
return getShapeManager()->getShape(shapeInfo);
|
||||||
}
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RELIABLE_SEND_HACK: until we have truly reliable resends of non-moving updates
|
// RELIABLE_SEND_HACK: until we have truly reliable resends of non-moving updates
|
||||||
|
@ -444,7 +447,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
|
||||||
_lastStep = step;
|
_lastStep = step;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() const {
|
uint32_t EntityMotionState::getAndClearIncomingDirtyFlags() {
|
||||||
uint32_t dirtyFlags = 0;
|
uint32_t dirtyFlags = 0;
|
||||||
if (_body && _entity) {
|
if (_body && _entity) {
|
||||||
dirtyFlags = _entity->getDirtyFlags();
|
dirtyFlags = _entity->getDirtyFlags();
|
||||||
|
|
|
@ -43,14 +43,12 @@ public:
|
||||||
// this relays outgoing position/rotation to the EntityItem
|
// this relays outgoing position/rotation to the EntityItem
|
||||||
virtual void setWorldTransform(const btTransform& worldTrans);
|
virtual void setWorldTransform(const btTransform& worldTrans);
|
||||||
|
|
||||||
virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo);
|
|
||||||
|
|
||||||
bool isCandidateForOwnership(const QUuid& sessionID) const;
|
bool isCandidateForOwnership(const QUuid& sessionID) const;
|
||||||
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
||||||
bool shouldSendUpdate(uint32_t simulationStep, const QUuid& sessionID);
|
bool shouldSendUpdate(uint32_t simulationStep, const QUuid& sessionID);
|
||||||
void sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step);
|
void sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step);
|
||||||
|
|
||||||
virtual uint32_t getAndClearIncomingDirtyFlags() const;
|
virtual uint32_t getAndClearIncomingDirtyFlags();
|
||||||
|
|
||||||
void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; }
|
void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; }
|
||||||
void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; }
|
void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; }
|
||||||
|
@ -62,7 +60,7 @@ public:
|
||||||
virtual float getObjectAngularDamping() const { return _entity->getAngularDamping(); }
|
virtual float getObjectAngularDamping() const { return _entity->getAngularDamping(); }
|
||||||
|
|
||||||
virtual glm::vec3 getObjectPosition() const { return _entity->getPosition() - ObjectMotionState::getWorldOffset(); }
|
virtual glm::vec3 getObjectPosition() const { return _entity->getPosition() - ObjectMotionState::getWorldOffset(); }
|
||||||
virtual const glm::quat& getObjectRotation() const { return _entity->getRotation(); }
|
virtual glm::quat getObjectRotation() const { return _entity->getRotation(); }
|
||||||
virtual const glm::vec3& getObjectLinearVelocity() const { return _entity->getVelocity(); }
|
virtual const glm::vec3& getObjectLinearVelocity() const { return _entity->getVelocity(); }
|
||||||
virtual const glm::vec3& getObjectAngularVelocity() const { return _entity->getAngularVelocity(); }
|
virtual const glm::vec3& getObjectAngularVelocity() const { return _entity->getAngularVelocity(); }
|
||||||
virtual const glm::vec3& getObjectGravity() const { return _entity->getGravity(); }
|
virtual const glm::vec3& getObjectGravity() const { return _entity->getGravity(); }
|
||||||
|
@ -82,8 +80,8 @@ public:
|
||||||
friend class PhysicalEntitySimulation;
|
friend class PhysicalEntitySimulation;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void clearEntity();
|
virtual btCollisionShape* computeNewShape();
|
||||||
|
virtual void clearObjectBackPointer();
|
||||||
virtual void setMotionType(MotionType motionType);
|
virtual void setMotionType(MotionType motionType);
|
||||||
|
|
||||||
EntityItemPointer _entity;
|
EntityItemPointer _entity;
|
||||||
|
|
|
@ -140,20 +140,14 @@ void ObjectMotionState::handleEasyChanges(uint32_t flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & EntityItem::DIRTY_MASS) {
|
if (flags & EntityItem::DIRTY_MASS) {
|
||||||
float mass = getMass();
|
updateBodyMassProperties();
|
||||||
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
|
||||||
_body->getCollisionShape()->calculateLocalInertia(mass, inertia);
|
|
||||||
_body->setMassProps(mass, inertia);
|
|
||||||
_body->updateInertiaTensor();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) {
|
void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) {
|
||||||
if (flags & EntityItem::DIRTY_SHAPE) {
|
if (flags & EntityItem::DIRTY_SHAPE) {
|
||||||
// make sure the new shape is valid
|
// make sure the new shape is valid
|
||||||
ShapeInfo shapeInfo;
|
btCollisionShape* newShape = computeNewShape();
|
||||||
computeObjectShapeInfo(shapeInfo);
|
|
||||||
btCollisionShape* newShape = getShapeManager()->getShape(shapeInfo);
|
|
||||||
if (!newShape) {
|
if (!newShape) {
|
||||||
qCDebug(physics) << "Warning: failed to generate new shape!";
|
qCDebug(physics) << "Warning: failed to generate new shape!";
|
||||||
// failed to generate new shape! --> keep old shape and remove shape-change flag
|
// failed to generate new shape! --> keep old shape and remove shape-change flag
|
||||||
|
@ -168,17 +162,22 @@ void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getShapeManager()->releaseShape(_shape);
|
getShapeManager()->releaseShape(_shape);
|
||||||
_shape = newShape;
|
if (_shape != newShape) {
|
||||||
_body->setCollisionShape(_shape);
|
_shape = newShape;
|
||||||
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
_body->setCollisionShape(_shape);
|
||||||
handleEasyChanges(flags);
|
} else {
|
||||||
}
|
// huh... the shape didn't actually change, so we clear the DIRTY_SHAPE flag
|
||||||
} else {
|
flags &= ~EntityItem::DIRTY_SHAPE;
|
||||||
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
|
||||||
handleEasyChanges(flags);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
engine->reinsertObject(this);
|
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||||
|
handleEasyChanges(flags);
|
||||||
|
}
|
||||||
|
// it is possible that there are no HARD flags at this point (if DIRTY_SHAPE was removed)
|
||||||
|
// so we check again befoe we reinsert:
|
||||||
|
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
|
||||||
|
engine->reinsertObject(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectMotionState::updateBodyMaterialProperties() {
|
void ObjectMotionState::updateBodyMaterialProperties() {
|
||||||
|
@ -193,3 +192,11 @@ void ObjectMotionState::updateBodyVelocities() {
|
||||||
setBodyGravity(getObjectGravity());
|
setBodyGravity(getObjectGravity());
|
||||||
_body->setActivationState(ACTIVE_TAG);
|
_body->setActivationState(ACTIVE_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::updateBodyMassProperties() {
|
||||||
|
float mass = getMass();
|
||||||
|
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
||||||
|
_body->getCollisionShape()->calculateLocalInertia(mass, inertia);
|
||||||
|
_body->setMassProps(mass, inertia);
|
||||||
|
_body->updateInertiaTensor();
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
#include <btBulletDynamicsCommon.h>
|
#include <btBulletDynamicsCommon.h>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include <EntityItem.h>
|
#include <EntityItem.h>
|
||||||
|
|
||||||
#include "ContactInfo.h"
|
#include "ContactInfo.h"
|
||||||
|
@ -27,9 +30,9 @@ enum MotionType {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MotionStateType {
|
enum MotionStateType {
|
||||||
MOTION_STATE_TYPE_INVALID,
|
MOTIONSTATE_TYPE_INVALID,
|
||||||
MOTION_STATE_TYPE_ENTITY,
|
MOTIONSTATE_TYPE_ENTITY,
|
||||||
MOTION_STATE_TYPE_AVATAR
|
MOTIONSTATE_TYPE_AVATAR
|
||||||
};
|
};
|
||||||
|
|
||||||
// The update flags trigger two varieties of updates: "hard" which require the body to be pulled
|
// The update flags trigger two varieties of updates: "hard" which require the body to be pulled
|
||||||
|
@ -71,8 +74,9 @@ public:
|
||||||
virtual void handleEasyChanges(uint32_t flags);
|
virtual void handleEasyChanges(uint32_t flags);
|
||||||
virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine);
|
virtual void handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine);
|
||||||
|
|
||||||
virtual void updateBodyMaterialProperties();
|
void updateBodyMaterialProperties();
|
||||||
virtual void updateBodyVelocities();
|
void updateBodyVelocities();
|
||||||
|
virtual void updateBodyMassProperties();
|
||||||
|
|
||||||
MotionStateType getType() const { return _type; }
|
MotionStateType getType() const { return _type; }
|
||||||
virtual MotionType getMotionType() const { return _motionType; }
|
virtual MotionType getMotionType() const { return _motionType; }
|
||||||
|
@ -87,11 +91,9 @@ public:
|
||||||
glm::vec3 getBodyLinearVelocity() const;
|
glm::vec3 getBodyLinearVelocity() const;
|
||||||
glm::vec3 getBodyAngularVelocity() const;
|
glm::vec3 getBodyAngularVelocity() const;
|
||||||
|
|
||||||
virtual uint32_t getAndClearIncomingDirtyFlags() const = 0;
|
virtual uint32_t getAndClearIncomingDirtyFlags() = 0;
|
||||||
|
|
||||||
virtual MotionType computeObjectMotionType() const = 0;
|
virtual MotionType computeObjectMotionType() const = 0;
|
||||||
virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo) = 0;
|
|
||||||
|
|
||||||
|
|
||||||
btCollisionShape* getShape() const { return _shape; }
|
btCollisionShape* getShape() const { return _shape; }
|
||||||
btRigidBody* getRigidBody() const { return _body; }
|
btRigidBody* getRigidBody() const { return _body; }
|
||||||
|
@ -109,7 +111,7 @@ public:
|
||||||
virtual float getObjectAngularDamping() const = 0;
|
virtual float getObjectAngularDamping() const = 0;
|
||||||
|
|
||||||
virtual glm::vec3 getObjectPosition() const = 0;
|
virtual glm::vec3 getObjectPosition() const = 0;
|
||||||
virtual const glm::quat& getObjectRotation() const = 0;
|
virtual glm::quat getObjectRotation() const = 0;
|
||||||
virtual const glm::vec3& getObjectLinearVelocity() const = 0;
|
virtual const glm::vec3& getObjectLinearVelocity() const = 0;
|
||||||
virtual const glm::vec3& getObjectAngularVelocity() const = 0;
|
virtual const glm::vec3& getObjectAngularVelocity() const = 0;
|
||||||
virtual const glm::vec3& getObjectGravity() const = 0;
|
virtual const glm::vec3& getObjectGravity() const = 0;
|
||||||
|
@ -124,10 +126,15 @@ public:
|
||||||
friend class PhysicsEngine;
|
friend class PhysicsEngine;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void setMotionType(MotionType motionType);
|
virtual btCollisionShape* computeNewShape() = 0;
|
||||||
|
void setMotionType(MotionType motionType);
|
||||||
|
|
||||||
|
// clearObjectBackPointer() overrrides should call the base method, then actually clear the object back pointer.
|
||||||
|
virtual void clearObjectBackPointer() { _type = MOTIONSTATE_TYPE_INVALID; }
|
||||||
|
|
||||||
void setRigidBody(btRigidBody* body);
|
void setRigidBody(btRigidBody* body);
|
||||||
|
|
||||||
MotionStateType _type = MOTION_STATE_TYPE_INVALID; // type of MotionState
|
MotionStateType _type = MOTIONSTATE_TYPE_INVALID; // type of MotionState
|
||||||
MotionType _motionType; // type of motion: KINEMATIC, DYNAMIC, or STATIC
|
MotionType _motionType; // type of motion: KINEMATIC, DYNAMIC, or STATIC
|
||||||
|
|
||||||
btCollisionShape* _shape;
|
btCollisionShape* _shape;
|
||||||
|
@ -137,4 +144,7 @@ protected:
|
||||||
uint32_t _lastKinematicStep;
|
uint32_t _lastKinematicStep;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef QSet<ObjectMotionState*> SetOfMotionStates;
|
||||||
|
typedef QVector<ObjectMotionState*> VectorOfMotionStates;
|
||||||
|
|
||||||
#endif // hifi_ObjectMotionState_h
|
#endif // hifi_ObjectMotionState_h
|
||||||
|
|
|
@ -9,11 +9,9 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "EntityMotionState.h"
|
|
||||||
#include "PhysicalEntitySimulation.h"
|
#include "PhysicalEntitySimulation.h"
|
||||||
#include "PhysicsHelpers.h"
|
#include "PhysicsHelpers.h"
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
#include "ShapeInfoUtil.h"
|
|
||||||
#include "ShapeManager.h"
|
#include "ShapeManager.h"
|
||||||
|
|
||||||
PhysicalEntitySimulation::PhysicalEntitySimulation() {
|
PhysicalEntitySimulation::PhysicalEntitySimulation() {
|
||||||
|
@ -25,7 +23,6 @@ PhysicalEntitySimulation::~PhysicalEntitySimulation() {
|
||||||
void PhysicalEntitySimulation::init(
|
void PhysicalEntitySimulation::init(
|
||||||
EntityTree* tree,
|
EntityTree* tree,
|
||||||
PhysicsEngine* physicsEngine,
|
PhysicsEngine* physicsEngine,
|
||||||
ShapeManager* shapeManager,
|
|
||||||
EntityEditPacketSender* packetSender) {
|
EntityEditPacketSender* packetSender) {
|
||||||
assert(tree);
|
assert(tree);
|
||||||
setEntityTree(tree);
|
setEntityTree(tree);
|
||||||
|
@ -33,9 +30,6 @@ void PhysicalEntitySimulation::init(
|
||||||
assert(physicsEngine);
|
assert(physicsEngine);
|
||||||
_physicsEngine = physicsEngine;
|
_physicsEngine = physicsEngine;
|
||||||
|
|
||||||
assert(shapeManager);
|
|
||||||
_shapeManager = shapeManager;
|
|
||||||
|
|
||||||
assert(packetSender);
|
assert(packetSender);
|
||||||
_entityPacketSender = packetSender;
|
_entityPacketSender = packetSender;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +54,7 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||||
void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
if (motionState) {
|
if (motionState) {
|
||||||
motionState->clearEntity();
|
motionState->clearObjectBackPointer();
|
||||||
entity->setPhysicsInfo(nullptr);
|
entity->setPhysicsInfo(nullptr);
|
||||||
_pendingRemoves.insert(motionState);
|
_pendingRemoves.insert(motionState);
|
||||||
_outgoingChanges.remove(motionState);
|
_outgoingChanges.remove(motionState);
|
||||||
|
@ -109,7 +103,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
|
||||||
if (entity) {
|
if (entity) {
|
||||||
entity->setPhysicsInfo(nullptr);
|
entity->setPhysicsInfo(nullptr);
|
||||||
}
|
}
|
||||||
motionState->clearEntity();
|
motionState->clearObjectBackPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// then delete the objects (aka MotionStates)
|
// then delete the objects (aka MotionStates)
|
||||||
|
@ -135,7 +129,7 @@ VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToDelete() {
|
||||||
if (entity) {
|
if (entity) {
|
||||||
_pendingAdds.remove(entity);
|
_pendingAdds.remove(entity);
|
||||||
entity->setPhysicsInfo(nullptr);
|
entity->setPhysicsInfo(nullptr);
|
||||||
motionState->clearEntity();
|
motionState->clearObjectBackPointer();
|
||||||
}
|
}
|
||||||
_tempVector.push_back(motionState);
|
_tempVector.push_back(motionState);
|
||||||
}
|
}
|
||||||
|
@ -158,16 +152,14 @@ VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToAdd() {
|
||||||
} else if (entity->isReadyToComputeShape()) {
|
} else if (entity->isReadyToComputeShape()) {
|
||||||
ShapeInfo shapeInfo;
|
ShapeInfo shapeInfo;
|
||||||
entity->computeShapeInfo(shapeInfo);
|
entity->computeShapeInfo(shapeInfo);
|
||||||
btCollisionShape* shape = _shapeManager->getShape(shapeInfo);
|
btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||||
if (shape) {
|
if (shape) {
|
||||||
EntityMotionState* motionState = new EntityMotionState(shape, entity);
|
EntityMotionState* motionState = new EntityMotionState(shape, entity);
|
||||||
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
||||||
motionState->setMass(entity->computeMass());
|
|
||||||
_physicalObjects.insert(motionState);
|
_physicalObjects.insert(motionState);
|
||||||
_tempVector.push_back(motionState);
|
_tempVector.push_back(motionState);
|
||||||
entityItr = _pendingAdds.erase(entityItr);
|
entityItr = _pendingAdds.erase(entityItr);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Seth to look into why this case is hit.
|
|
||||||
//qDebug() << "Warning! Failed to generate new shape for entity." << entity->getName();
|
//qDebug() << "Warning! Failed to generate new shape for entity." << entity->getName();
|
||||||
++entityItr;
|
++entityItr;
|
||||||
}
|
}
|
||||||
|
@ -192,7 +184,7 @@ void PhysicalEntitySimulation::handleOutgoingChanges(VectorOfMotionStates& motio
|
||||||
// walk the motionStates looking for those that correspond to entities
|
// walk the motionStates looking for those that correspond to entities
|
||||||
for (auto stateItr : motionStates) {
|
for (auto stateItr : motionStates) {
|
||||||
ObjectMotionState* state = &(*stateItr);
|
ObjectMotionState* state = &(*stateItr);
|
||||||
if (state && state->getType() == MOTION_STATE_TYPE_ENTITY) {
|
if (state && state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||||
EntityItemPointer entity = entityState->getEntity();
|
EntityItemPointer entity = entityState->getEntity();
|
||||||
if (entity) {
|
if (entity) {
|
||||||
|
|
|
@ -21,10 +21,7 @@
|
||||||
#include <EntitySimulation.h>
|
#include <EntitySimulation.h>
|
||||||
|
|
||||||
#include "PhysicsEngine.h"
|
#include "PhysicsEngine.h"
|
||||||
#include "PhysicsTypedefs.h"
|
#include "EntityMotionState.h"
|
||||||
|
|
||||||
class EntityMotionState;
|
|
||||||
class ShapeManager;
|
|
||||||
|
|
||||||
typedef QSet<EntityMotionState*> SetOfEntityMotionStates;
|
typedef QSet<EntityMotionState*> SetOfEntityMotionStates;
|
||||||
|
|
||||||
|
@ -33,7 +30,7 @@ public:
|
||||||
PhysicalEntitySimulation();
|
PhysicalEntitySimulation();
|
||||||
~PhysicalEntitySimulation();
|
~PhysicalEntitySimulation();
|
||||||
|
|
||||||
void init(EntityTree* tree, PhysicsEngine* engine, ShapeManager* shapeManager, EntityEditPacketSender* packetSender);
|
void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender);
|
||||||
|
|
||||||
protected: // only called by EntitySimulation
|
protected: // only called by EntitySimulation
|
||||||
// overrides for EntitySimulation
|
// overrides for EntitySimulation
|
||||||
|
@ -63,7 +60,6 @@ private:
|
||||||
SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine
|
SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine
|
||||||
VectorOfMotionStates _tempVector; // temporary array reference, valid immediately after getObjectsToRemove() (and friends)
|
VectorOfMotionStates _tempVector; // temporary array reference, valid immediately after getObjectsToRemove() (and friends)
|
||||||
|
|
||||||
ShapeManager* _shapeManager = nullptr;
|
|
||||||
PhysicsEngine* _physicsEngine = nullptr;
|
PhysicsEngine* _physicsEngine = nullptr;
|
||||||
EntityEditPacketSender* _entityPacketSender = nullptr;
|
EntityEditPacketSender* _entityPacketSender = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -318,16 +318,16 @@ CollisionEvents& PhysicsEngine::getCollisionEvents() {
|
||||||
ObjectMotionState* A = static_cast<ObjectMotionState*>(contactItr->first._a);
|
ObjectMotionState* A = static_cast<ObjectMotionState*>(contactItr->first._a);
|
||||||
ObjectMotionState* B = static_cast<ObjectMotionState*>(contactItr->first._b);
|
ObjectMotionState* B = static_cast<ObjectMotionState*>(contactItr->first._b);
|
||||||
|
|
||||||
if (A && A->getType() == MOTION_STATE_TYPE_ENTITY) {
|
if (A && A->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||||
QUuid idA = A->getObjectID();
|
QUuid idA = A->getObjectID();
|
||||||
QUuid idB;
|
QUuid idB;
|
||||||
if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) {
|
if (B && B->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||||
idB = B->getObjectID();
|
idB = B->getObjectID();
|
||||||
}
|
}
|
||||||
glm::vec3 position = bulletToGLM(contact.getPositionWorldOnB()) + _originOffset;
|
glm::vec3 position = bulletToGLM(contact.getPositionWorldOnB()) + _originOffset;
|
||||||
glm::vec3 penetration = bulletToGLM(contact.distance * contact.normalWorldOnB);
|
glm::vec3 penetration = bulletToGLM(contact.distance * contact.normalWorldOnB);
|
||||||
_collisionEvents.push_back(Collision(type, idA, idB, position, penetration));
|
_collisionEvents.push_back(Collision(type, idA, idB, position, penetration));
|
||||||
} else if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) {
|
} else if (B && B->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||||
QUuid idB = B->getObjectID();
|
QUuid idB = B->getObjectID();
|
||||||
glm::vec3 position = bulletToGLM(contact.getPositionWorldOnA()) + _originOffset;
|
glm::vec3 position = bulletToGLM(contact.getPositionWorldOnA()) + _originOffset;
|
||||||
// NOTE: we're flipping the order of A and B (so that the first objectID is never NULL)
|
// NOTE: we're flipping the order of A and B (so that the first objectID is never NULL)
|
||||||
|
|
|
@ -22,13 +22,11 @@
|
||||||
#include "BulletUtil.h"
|
#include "BulletUtil.h"
|
||||||
#include "ContactInfo.h"
|
#include "ContactInfo.h"
|
||||||
#include "DynamicCharacterController.h"
|
#include "DynamicCharacterController.h"
|
||||||
#include "PhysicsTypedefs.h"
|
#include "ObjectMotionState.h"
|
||||||
#include "ThreadSafeDynamicsWorld.h"
|
#include "ThreadSafeDynamicsWorld.h"
|
||||||
|
|
||||||
const float HALF_SIMULATION_EXTENT = 512.0f; // meters
|
const float HALF_SIMULATION_EXTENT = 512.0f; // meters
|
||||||
|
|
||||||
class ObjectMotionState;
|
|
||||||
|
|
||||||
// simple class for keeping track of contacts
|
// simple class for keeping track of contacts
|
||||||
class ContactKey {
|
class ContactKey {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
//
|
|
||||||
// PhysicsTypedefs.h
|
|
||||||
// libraries/physcis/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows 2015.04.29
|
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// Distributed under the Apache License, Version 2.0.
|
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef hifi_PhysicsTypedefs_h
|
|
||||||
#define hifi_PhysicsTypedefs_h
|
|
||||||
|
|
||||||
#include <QSet>
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
class ObjectMotionState;
|
|
||||||
|
|
||||||
typedef QSet<ObjectMotionState*> SetOfMotionStates;
|
|
||||||
typedef QVector<ObjectMotionState*> VectorOfMotionStates;
|
|
||||||
|
|
||||||
#endif //hifi_PhysicsTypedefs_h
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// ShapeInfoUtil.cpp
|
// ShapeFactory.cpp
|
||||||
// libraries/physcis/src
|
// libraries/physcis/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.12.01
|
// Created by Andrew Meadows 2014.12.01
|
||||||
|
@ -9,14 +9,16 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
#include <SharedUtil.h> // for MILLIMETERS_PER_METER
|
#include <SharedUtil.h> // for MILLIMETERS_PER_METER
|
||||||
|
|
||||||
#include "ShapeInfoUtil.h"
|
#include "ShapeFactory.h"
|
||||||
#include "BulletUtil.h"
|
#include "BulletUtil.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
btConvexHullShape* ShapeInfoUtil::createConvexHull(const QVector<glm::vec3>& points) {
|
btConvexHullShape* ShapeFactory::createConvexHull(const QVector<glm::vec3>& points) {
|
||||||
assert(points.size() > 0);
|
assert(points.size() > 0);
|
||||||
|
|
||||||
btConvexHullShape* hull = new btConvexHullShape();
|
btConvexHullShape* hull = new btConvexHullShape();
|
||||||
|
@ -57,9 +59,10 @@ btConvexHullShape* ShapeInfoUtil::createConvexHull(const QVector<glm::vec3>& poi
|
||||||
return hull;
|
return hull;
|
||||||
}
|
}
|
||||||
|
|
||||||
btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
|
btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
||||||
btCollisionShape* shape = NULL;
|
btCollisionShape* shape = NULL;
|
||||||
switch(info.getType()) {
|
int type = info.getType();
|
||||||
|
switch(type) {
|
||||||
case SHAPE_TYPE_BOX: {
|
case SHAPE_TYPE_BOX: {
|
||||||
shape = new btBoxShape(glmToBullet(info.getHalfExtents()));
|
shape = new btBoxShape(glmToBullet(info.getHalfExtents()));
|
||||||
}
|
}
|
||||||
|
@ -95,5 +98,17 @@ btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (shape && type != SHAPE_TYPE_COMPOUND) {
|
||||||
|
if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) {
|
||||||
|
// this shape has an offset, which we support by wrapping the true shape
|
||||||
|
// in a btCompoundShape with a local transform
|
||||||
|
auto compound = new btCompoundShape();
|
||||||
|
btTransform trans;
|
||||||
|
trans.setIdentity();
|
||||||
|
trans.setOrigin(glmToBullet(info.getOffset()));
|
||||||
|
compound->addChildShape(trans, shape);
|
||||||
|
shape = compound;
|
||||||
|
}
|
||||||
|
}
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// ShapeInfoUtil.h
|
// ShapeFactory.h
|
||||||
// libraries/physcis/src
|
// libraries/physcis/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.12.01
|
// Created by Andrew Meadows 2014.12.01
|
||||||
|
@ -9,8 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef hifi_ShapeInfoUtil_h
|
#ifndef hifi_ShapeFactory_h
|
||||||
#define hifi_ShapeInfoUtil_h
|
#define hifi_ShapeFactory_h
|
||||||
|
|
||||||
#include <btBulletDynamicsCommon.h>
|
#include <btBulletDynamicsCommon.h>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
@ -20,11 +20,11 @@
|
||||||
// translates between ShapeInfo and btShape
|
// translates between ShapeInfo and btShape
|
||||||
|
|
||||||
// TODO: rename this to ShapeFactory
|
// TODO: rename this to ShapeFactory
|
||||||
namespace ShapeInfoUtil {
|
namespace ShapeFactory {
|
||||||
|
|
||||||
btConvexHullShape* createConvexHull(const QVector<glm::vec3>& points);
|
btConvexHullShape* createConvexHull(const QVector<glm::vec3>& points);
|
||||||
|
|
||||||
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ShapeInfoUtil_h
|
#endif // hifi_ShapeFactory_h
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
#include "ShapeInfoUtil.h"
|
#include "ShapeFactory.h"
|
||||||
#include "ShapeManager.h"
|
#include "ShapeManager.h"
|
||||||
|
|
||||||
ShapeManager::ShapeManager() {
|
ShapeManager::ShapeManager() {
|
||||||
|
@ -46,7 +46,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
shapeRef->refCount++;
|
shapeRef->refCount++;
|
||||||
return shapeRef->shape;
|
return shapeRef->shape;
|
||||||
}
|
}
|
||||||
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
|
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
if (shape) {
|
if (shape) {
|
||||||
ShapeReference newRef;
|
ShapeReference newRef;
|
||||||
newRef.refCount = 1;
|
newRef.refCount = 1;
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
#include <LinearMath/btQuickprof.h>
|
#include <LinearMath/btQuickprof.h>
|
||||||
|
|
||||||
#include "ObjectMotionState.h"
|
|
||||||
#include "ThreadSafeDynamicsWorld.h"
|
#include "ThreadSafeDynamicsWorld.h"
|
||||||
|
|
||||||
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#include <BulletDynamics/Dynamics/btRigidBody.h>
|
#include <BulletDynamics/Dynamics/btRigidBody.h>
|
||||||
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
|
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
|
||||||
|
|
||||||
#include "PhysicsTypedefs.h"
|
#include "ObjectMotionState.h"
|
||||||
|
|
||||||
ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld {
|
ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
void ShapeInfo::clear() {
|
void ShapeInfo::clear() {
|
||||||
_type = SHAPE_TYPE_NONE;
|
_type = SHAPE_TYPE_NONE;
|
||||||
_halfExtents = glm::vec3(0.0f);
|
_halfExtents = _offset = glm::vec3(0.0f);
|
||||||
_doubleHashKey.clear();
|
_doubleHashKey.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
|
||||||
_halfExtents = halfExtents;
|
_halfExtents = halfExtents;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
_doubleHashKey.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
||||||
|
@ -85,6 +86,11 @@ void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
|
||||||
_doubleHashKey.clear();
|
_doubleHashKey.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShapeInfo::setOffset(const glm::vec3& offset) {
|
||||||
|
_offset = offset;
|
||||||
|
_doubleHashKey.clear();
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t ShapeInfo::getNumSubShapes() const {
|
uint32_t ShapeInfo::getNumSubShapes() const {
|
||||||
if (_type == SHAPE_TYPE_NONE) {
|
if (_type == SHAPE_TYPE_NONE) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -175,6 +181,7 @@ bool ShapeInfo::contains(const glm::vec3& point) const {
|
||||||
const DoubleHashKey& ShapeInfo::getHash() const {
|
const DoubleHashKey& ShapeInfo::getHash() const {
|
||||||
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
|
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
|
||||||
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
|
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
|
||||||
|
bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET;
|
||||||
// The key is not yet cached therefore we must compute it! To this end we bypass the const-ness
|
// The key is not yet cached therefore we must compute it! To this end we bypass the const-ness
|
||||||
// of this method by grabbing a non-const pointer to "this" and a non-const reference to _doubleHashKey.
|
// of this method by grabbing a non-const pointer to "this" and a non-const reference to _doubleHashKey.
|
||||||
ShapeInfo* thisPtr = const_cast<ShapeInfo*>(this);
|
ShapeInfo* thisPtr = const_cast<ShapeInfo*>(this);
|
||||||
|
@ -190,9 +197,14 @@ const DoubleHashKey& ShapeInfo::getHash() const {
|
||||||
for (int j = 0; j < 3; ++j) {
|
for (int j = 0; j < 3; ++j) {
|
||||||
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
||||||
// so the cast to int produces a round() effect rather than a floor()
|
// so the cast to int produces a round() effect rather than a floor()
|
||||||
uint32_t floatHash =
|
hash ^= DoubleHashKey::hashFunction(
|
||||||
DoubleHashKey::hashFunction((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), primeIndex++);
|
(uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f),
|
||||||
hash ^= floatHash;
|
primeIndex++);
|
||||||
|
if (useOffset) {
|
||||||
|
hash ^= DoubleHashKey::hashFunction(
|
||||||
|
(uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f),
|
||||||
|
primeIndex++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
key.setHash(hash);
|
key.setHash(hash);
|
||||||
|
|
||||||
|
@ -201,8 +213,12 @@ const DoubleHashKey& ShapeInfo::getHash() const {
|
||||||
for (int j = 0; j < 3; ++j) {
|
for (int j = 0; j < 3; ++j) {
|
||||||
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
||||||
// so the cast to int produces a round() effect rather than a floor()
|
// so the cast to int produces a round() effect rather than a floor()
|
||||||
uint32_t floatHash =
|
uint32_t floatHash = DoubleHashKey::hashFunction2(
|
||||||
DoubleHashKey::hashFunction2((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f));
|
(uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f));
|
||||||
|
if (useOffset) {
|
||||||
|
floatHash ^= DoubleHashKey::hashFunction2(
|
||||||
|
(uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f));
|
||||||
|
}
|
||||||
hash += ~(floatHash << 17);
|
hash += ~(floatHash << 17);
|
||||||
hash ^= (floatHash >> 11);
|
hash ^= (floatHash >> 11);
|
||||||
hash += (floatHash << 4);
|
hash += (floatHash << 4);
|
||||||
|
|
|
@ -16,9 +16,12 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
#include "DoubleHashKey.h"
|
#include "DoubleHashKey.h"
|
||||||
|
|
||||||
|
const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored
|
||||||
|
|
||||||
enum ShapeType {
|
enum ShapeType {
|
||||||
SHAPE_TYPE_NONE,
|
SHAPE_TYPE_NONE,
|
||||||
SHAPE_TYPE_BOX,
|
SHAPE_TYPE_BOX,
|
||||||
|
@ -46,10 +49,12 @@ public:
|
||||||
void setEllipsoid(const glm::vec3& halfExtents);
|
void setEllipsoid(const glm::vec3& halfExtents);
|
||||||
void setConvexHulls(const QVector<QVector<glm::vec3>>& points);
|
void setConvexHulls(const QVector<QVector<glm::vec3>>& points);
|
||||||
void setCapsuleY(float radius, float halfHeight);
|
void setCapsuleY(float radius, float halfHeight);
|
||||||
|
void setOffset(const glm::vec3& offset);
|
||||||
|
|
||||||
int getType() const { return _type; }
|
int getType() const { return _type; }
|
||||||
|
|
||||||
const glm::vec3& getHalfExtents() const { return _halfExtents; }
|
const glm::vec3& getHalfExtents() const { return _halfExtents; }
|
||||||
|
const glm::vec3& getOffset() const { return _offset; }
|
||||||
|
|
||||||
const QVector<QVector<glm::vec3>>& getPoints() const { return _points; }
|
const QVector<QVector<glm::vec3>>& getPoints() const { return _points; }
|
||||||
uint32_t getNumSubShapes() const;
|
uint32_t getNumSubShapes() const;
|
||||||
|
@ -68,6 +73,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
ShapeType _type = SHAPE_TYPE_NONE;
|
ShapeType _type = SHAPE_TYPE_NONE;
|
||||||
glm::vec3 _halfExtents = glm::vec3(0.0f);
|
glm::vec3 _halfExtents = glm::vec3(0.0f);
|
||||||
|
glm::vec3 _offset = glm::vec3(0.0f);
|
||||||
DoubleHashKey _doubleHashKey;
|
DoubleHashKey _doubleHashKey;
|
||||||
QVector<QVector<glm::vec3>> _points; // points for convex collision hulls
|
QVector<QVector<glm::vec3>> _points; // points for convex collision hulls
|
||||||
QUrl _url; // url for model of convex collision hulls
|
QUrl _url; // url for model of convex collision hulls
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
#include <DoubleHashKey.h>
|
#include <DoubleHashKey.h>
|
||||||
#include <ShapeInfo.h>
|
#include <ShapeInfo.h>
|
||||||
#include <ShapeInfoUtil.h>
|
#include <ShapeFactory.h>
|
||||||
#include <StreamUtils.h>
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
#include "ShapeInfoTests.h"
|
#include "ShapeInfoTests.h"
|
||||||
|
@ -142,7 +142,7 @@ void ShapeInfoTests::testBoxShape() {
|
||||||
info.setBox(halfExtents);
|
info.setBox(halfExtents);
|
||||||
DoubleHashKey key = info.getHash();
|
DoubleHashKey key = info.getHash();
|
||||||
|
|
||||||
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
|
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
if (!shape) {
|
if (!shape) {
|
||||||
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: NULL Box shape" << std::endl;
|
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: NULL Box shape" << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ void ShapeInfoTests::testSphereShape() {
|
||||||
info.setSphere(radius);
|
info.setSphere(radius);
|
||||||
DoubleHashKey key = info.getHash();
|
DoubleHashKey key = info.getHash();
|
||||||
|
|
||||||
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
|
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
|
|
||||||
ShapeInfo otherInfo = info;
|
ShapeInfo otherInfo = info;
|
||||||
DoubleHashKey otherKey = otherInfo.getHash();
|
DoubleHashKey otherKey = otherInfo.getHash();
|
||||||
|
@ -192,7 +192,7 @@ void ShapeInfoTests::testCylinderShape() {
|
||||||
info.setCylinder(radius, height);
|
info.setCylinder(radius, height);
|
||||||
DoubleHashKey key = info.getHash();
|
DoubleHashKey key = info.getHash();
|
||||||
|
|
||||||
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
|
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
|
|
||||||
ShapeInfo otherInfo = info;
|
ShapeInfo otherInfo = info;
|
||||||
DoubleHashKey otherKey = otherInfo.getHash();
|
DoubleHashKey otherKey = otherInfo.getHash();
|
||||||
|
@ -217,7 +217,7 @@ void ShapeInfoTests::testCapsuleShape() {
|
||||||
info.setCapsule(radius, height);
|
info.setCapsule(radius, height);
|
||||||
DoubleHashKey key = info.getHash();
|
DoubleHashKey key = info.getHash();
|
||||||
|
|
||||||
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
|
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
|
|
||||||
ShapeInfo otherInfo = info;
|
ShapeInfo otherInfo = info;
|
||||||
DoubleHashKey otherKey = otherInfo.getHash();
|
DoubleHashKey otherKey = otherInfo.getHash();
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ShapeInfoUtil.h>
|
|
||||||
#include <ShapeManager.h>
|
#include <ShapeManager.h>
|
||||||
#include <StreamUtils.h>
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue