mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #15511 from AndrewMeadows/build-shapes-off-mainloop
case 22166: Build complex shapes in ThreadPool instead of MainLoop
This commit is contained in:
commit
930032bcc6
54 changed files with 858 additions and 702 deletions
|
@ -37,6 +37,7 @@ void EntityTreeHeadlessViewer::update() {
|
||||||
if (_tree) {
|
if (_tree) {
|
||||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||||
tree->withTryWriteLock([&] {
|
tree->withTryWriteLock([&] {
|
||||||
|
tree->preUpdate();
|
||||||
tree->update();
|
tree->update();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -2790,21 +2790,15 @@ Application::~Application() {
|
||||||
// remove avatars from physics engine
|
// remove avatars from physics engine
|
||||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
avatarManager->clearOtherAvatars();
|
avatarManager->clearOtherAvatars();
|
||||||
|
auto myCharacterController = getMyAvatar()->getCharacterController();
|
||||||
|
myCharacterController->clearDetailedMotionStates();
|
||||||
|
|
||||||
PhysicsEngine::Transaction transaction;
|
PhysicsEngine::Transaction transaction;
|
||||||
avatarManager->buildPhysicsTransaction(transaction);
|
avatarManager->buildPhysicsTransaction(transaction);
|
||||||
_physicsEngine->processTransaction(transaction);
|
_physicsEngine->processTransaction(transaction);
|
||||||
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
||||||
|
|
||||||
avatarManager->deleteAllAvatars();
|
avatarManager->deleteAllAvatars();
|
||||||
|
|
||||||
auto myCharacterController = getMyAvatar()->getCharacterController();
|
|
||||||
myCharacterController->clearDetailedMotionStates();
|
|
||||||
|
|
||||||
myCharacterController->buildPhysicsTransaction(transaction);
|
|
||||||
_physicsEngine->processTransaction(transaction);
|
|
||||||
myCharacterController->handleProcessedPhysicsTransaction(transaction);
|
|
||||||
|
|
||||||
_physicsEngine->setCharacterController(nullptr);
|
_physicsEngine->setCharacterController(nullptr);
|
||||||
|
|
||||||
// the _shapeManager should have zero references
|
// the _shapeManager should have zero references
|
||||||
|
@ -6414,64 +6408,42 @@ void Application::update(float deltaTime) {
|
||||||
PROFILE_RANGE(simulation_physics, "Simulation");
|
PROFILE_RANGE(simulation_physics, "Simulation");
|
||||||
PerformanceTimer perfTimer("simulation");
|
PerformanceTimer perfTimer("simulation");
|
||||||
|
|
||||||
if (_physicsEnabled) {
|
getEntities()->preUpdate();
|
||||||
auto t0 = std::chrono::high_resolution_clock::now();
|
|
||||||
auto t1 = t0;
|
auto t0 = std::chrono::high_resolution_clock::now();
|
||||||
|
auto t1 = t0;
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(simulation_physics, "PrePhysics");
|
||||||
|
PerformanceTimer perfTimer("prePhysics)");
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "PrePhysics");
|
PROFILE_RANGE(simulation_physics, "Entities");
|
||||||
PerformanceTimer perfTimer("prePhysics)");
|
PhysicsEngine::Transaction transaction;
|
||||||
{
|
_entitySimulation->buildPhysicsTransaction(transaction);
|
||||||
PROFILE_RANGE(simulation_physics, "RemoveEntities");
|
_physicsEngine->processTransaction(transaction);
|
||||||
const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics();
|
_entitySimulation->handleProcessedPhysicsTransaction(transaction);
|
||||||
{
|
}
|
||||||
PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
|
|
||||||
_physicsEngine->removeObjects(motionStates);
|
|
||||||
}
|
|
||||||
_entitySimulation->deleteObjectsRemovedFromPhysics();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
t1 = std::chrono::high_resolution_clock::now();
|
||||||
PROFILE_RANGE(simulation_physics, "AddEntities");
|
|
||||||
VectorOfMotionStates motionStates;
|
|
||||||
getEntities()->getTree()->withReadLock([&] {
|
|
||||||
_entitySimulation->getObjectsToAddToPhysics(motionStates);
|
|
||||||
PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
|
|
||||||
_physicsEngine->addObjects(motionStates);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
{
|
|
||||||
VectorOfMotionStates motionStates;
|
|
||||||
PROFILE_RANGE(simulation_physics, "ChangeEntities");
|
|
||||||
getEntities()->getTree()->withReadLock([&] {
|
|
||||||
_entitySimulation->getObjectsToChange(motionStates);
|
|
||||||
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
|
|
||||||
_entitySimulation->setObjectsToChange(stillNeedChange);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(simulation_physics, "Avatars");
|
||||||
|
PhysicsEngine::Transaction transaction;
|
||||||
|
avatarManager->buildPhysicsTransaction(transaction);
|
||||||
|
_physicsEngine->processTransaction(transaction);
|
||||||
|
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
||||||
|
|
||||||
|
myAvatar->prepareForPhysicsSimulation();
|
||||||
|
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_physicsEnabled) {
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(simulation_physics, "PrepareActions");
|
||||||
_entitySimulation->applyDynamicChanges();
|
_entitySimulation->applyDynamicChanges();
|
||||||
|
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||||
t1 = std::chrono::high_resolution_clock::now();
|
dynamic->prepareForPhysicsSimulation();
|
||||||
|
});
|
||||||
{
|
|
||||||
PROFILE_RANGE(simulation_physics, "Avatars");
|
|
||||||
PhysicsEngine::Transaction transaction;
|
|
||||||
avatarManager->buildPhysicsTransaction(transaction);
|
|
||||||
_physicsEngine->processTransaction(transaction);
|
|
||||||
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
|
||||||
myAvatar->getCharacterController()->buildPhysicsTransaction(transaction);
|
|
||||||
_physicsEngine->processTransaction(transaction);
|
|
||||||
myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
|
|
||||||
myAvatar->prepareForPhysicsSimulation();
|
|
||||||
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
PROFILE_RANGE(simulation_physics, "PrepareActions");
|
|
||||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
|
||||||
dynamic->prepareForPhysicsSimulation();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
auto t2 = std::chrono::high_resolution_clock::now();
|
auto t2 = std::chrono::high_resolution_clock::now();
|
||||||
{
|
{
|
||||||
|
|
|
@ -101,7 +101,7 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
|
||||||
}
|
}
|
||||||
|
|
||||||
AvatarManager::~AvatarManager() {
|
AvatarManager::~AvatarManager() {
|
||||||
assert(_avatarsToChangeInPhysics.empty());
|
assert(_otherAvatarsToChangeInPhysics.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::init() {
|
void AvatarManager::init() {
|
||||||
|
@ -295,7 +295,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
|
|
||||||
render::Transaction renderTransaction;
|
render::Transaction renderTransaction;
|
||||||
workload::Transaction workloadTransaction;
|
workload::Transaction workloadTransaction;
|
||||||
|
|
||||||
for (int p = kHero; p < NumVariants; p++) {
|
for (int p = kHero; p < NumVariants; p++) {
|
||||||
auto& priorityQueue = avatarPriorityQueues[p];
|
auto& priorityQueue = avatarPriorityQueues[p];
|
||||||
// Sorting the current queue HERE as part of the measured timing.
|
// Sorting the current queue HERE as part of the measured timing.
|
||||||
|
@ -314,7 +314,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
// remove the orb if it is there
|
// remove the orb if it is there
|
||||||
avatar->removeOrb();
|
avatar->removeOrb();
|
||||||
if (avatar->needsPhysicsUpdate()) {
|
if (avatar->needsPhysicsUpdate()) {
|
||||||
_avatarsToChangeInPhysics.insert(avatar);
|
_otherAvatarsToChangeInPhysics.insert(avatar);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
avatar->updateOrbPosition();
|
avatar->updateOrbPosition();
|
||||||
|
@ -419,69 +419,111 @@ AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::queuePhysicsChange(const OtherAvatarPointer& avatar) {
|
void AvatarManager::queuePhysicsChange(const OtherAvatarPointer& avatar) {
|
||||||
_avatarsToChangeInPhysics.insert(avatar);
|
_otherAvatarsToChangeInPhysics.insert(avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
DetailedMotionState* AvatarManager::createDetailedMotionState(OtherAvatarPointer avatar, int32_t jointIndex) {
|
||||||
|
bool isBound = false;
|
||||||
|
std::vector<int32_t> boundJoints;
|
||||||
|
const btCollisionShape* shape = avatar->createCollisionShape(jointIndex, isBound, boundJoints);
|
||||||
|
if (shape) {
|
||||||
|
DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex);
|
||||||
|
motionState->setMass(0.0f); // DetailedMotionState has KINEMATIC MotionType, so zero mass is ok
|
||||||
|
motionState->setIsBound(isBound, boundJoints);
|
||||||
|
return motionState;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarManager::rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction, OtherAvatarPointer avatar) {
|
||||||
|
if (!avatar->_motionState) {
|
||||||
|
avatar->_motionState = new AvatarMotionState(avatar, nullptr);
|
||||||
|
}
|
||||||
|
AvatarMotionState* motionState = avatar->_motionState;
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
|
avatar->computeShapeInfo(shapeInfo);
|
||||||
|
const btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||||
|
assert(shape);
|
||||||
|
motionState->setShape(shape);
|
||||||
|
motionState->setMass(avatar->computeMass());
|
||||||
|
if (motionState->getRigidBody()) {
|
||||||
|
transaction.objectsToReinsert.push_back(motionState);
|
||||||
|
} else {
|
||||||
|
transaction.objectsToAdd.push_back(motionState);
|
||||||
|
}
|
||||||
|
motionState->clearIncomingDirtyFlags();
|
||||||
|
|
||||||
|
// Rather than reconcile numbers of joints after change to model or LOD
|
||||||
|
// we blow away old detailedMotionStates and create anew all around.
|
||||||
|
|
||||||
|
// delete old detailedMotionStates
|
||||||
|
auto& detailedMotionStates = avatar->getDetailedMotionStates();
|
||||||
|
if (detailedMotionStates.size() != 0) {
|
||||||
|
for (auto& detailedMotionState : detailedMotionStates) {
|
||||||
|
transaction.objectsToRemove.push_back(detailedMotionState);
|
||||||
|
}
|
||||||
|
avatar->resetDetailedMotionStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
// build new detailedMotionStates
|
||||||
|
OtherAvatar::BodyLOD lod = avatar->getBodyLOD();
|
||||||
|
if (lod == OtherAvatar::BodyLOD::Sphere) {
|
||||||
|
auto dMotionState = createDetailedMotionState(avatar, -1);
|
||||||
|
if (dMotionState) {
|
||||||
|
detailedMotionStates.push_back(dMotionState);
|
||||||
|
transaction.objectsToAdd.push_back(dMotionState);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int32_t numJoints = avatar->getJointCount();
|
||||||
|
for (int32_t i = 0; i < numJoints; i++) {
|
||||||
|
auto dMotionState = createDetailedMotionState(avatar, i);
|
||||||
|
if (dMotionState) {
|
||||||
|
detailedMotionStates.push_back(dMotionState);
|
||||||
|
transaction.objectsToAdd.push_back(dMotionState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avatar->_needsReinsertion = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||||
SetOfOtherAvatars failedShapeBuilds;
|
_myAvatar->getCharacterController()->buildPhysicsTransaction(transaction);
|
||||||
for (auto avatar : _avatarsToChangeInPhysics) {
|
for (auto avatar : _otherAvatarsToChangeInPhysics) {
|
||||||
bool isInPhysics = avatar->isInPhysicsSimulation();
|
bool isInPhysics = avatar->isInPhysicsSimulation();
|
||||||
if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) {
|
if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) {
|
||||||
if (isInPhysics) {
|
if (isInPhysics) {
|
||||||
transaction.objectsToRemove.push_back(avatar->_motionState);
|
transaction.objectsToRemove.push_back(avatar->_motionState);
|
||||||
avatar->_motionState = nullptr;
|
avatar->_motionState = nullptr;
|
||||||
auto& detailedMotionStates = avatar->getDetailedMotionStates();
|
auto& detailedMotionStates = avatar->getDetailedMotionStates();
|
||||||
for (auto& mState : detailedMotionStates) {
|
for (auto& motionState : detailedMotionStates) {
|
||||||
transaction.objectsToRemove.push_back(mState);
|
transaction.objectsToRemove.push_back(motionState);
|
||||||
}
|
}
|
||||||
avatar->resetDetailedMotionStates();
|
avatar->resetDetailedMotionStates();
|
||||||
} else {
|
} else {
|
||||||
if (avatar->getDetailedMotionStates().size() == 0) {
|
rebuildAvatarPhysics(transaction, avatar);
|
||||||
avatar->createDetailedMotionStates(avatar);
|
|
||||||
for (auto dMotionState : avatar->getDetailedMotionStates()) {
|
|
||||||
transaction.objectsToAdd.push_back(dMotionState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (avatar->getDetailedMotionStates().size() > 0) {
|
|
||||||
ShapeInfo shapeInfo;
|
|
||||||
avatar->computeShapeInfo(shapeInfo);
|
|
||||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
|
||||||
if (shape) {
|
|
||||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
|
||||||
motionState->setMass(avatar->computeMass());
|
|
||||||
avatar->_motionState = motionState;
|
|
||||||
transaction.objectsToAdd.push_back(motionState);
|
|
||||||
} else {
|
|
||||||
failedShapeBuilds.insert(avatar);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
failedShapeBuilds.insert(avatar);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (isInPhysics) {
|
} else if (isInPhysics) {
|
||||||
transaction.objectsToChange.push_back(avatar->_motionState);
|
AvatarMotionState* motionState = avatar->_motionState;
|
||||||
|
uint32_t flags = motionState->getIncomingDirtyFlags();
|
||||||
auto& detailedMotionStates = avatar->getDetailedMotionStates();
|
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||||
for (auto& mState : detailedMotionStates) {
|
motionState->handleEasyChanges(flags);
|
||||||
if (mState) {
|
}
|
||||||
transaction.objectsToChange.push_back(mState);
|
// NOTE: we don't call detailedMotionState->handleEasyChanges() here because they are KINEMATIC
|
||||||
}
|
// and Bullet will automagically call DetailedMotionState::getWorldTransform() on all that are active.
|
||||||
|
|
||||||
|
if (motionState->needsNewShape()) {
|
||||||
|
rebuildAvatarPhysics(transaction, avatar);
|
||||||
|
} else {
|
||||||
|
if (flags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP)) {
|
||||||
|
transaction.objectsToReinsert.push_back(motionState);
|
||||||
|
}
|
||||||
|
motionState->clearIncomingDirtyFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_avatarsToChangeInPhysics.swap(failedShapeBuilds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
void AvatarManager::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||||
// things on objectsToChange correspond to failed changes
|
|
||||||
// so we push them back onto _avatarsToChangeInPhysics
|
|
||||||
for (auto object : transaction.objectsToChange) {
|
|
||||||
AvatarMotionState* motionState = static_cast<AvatarMotionState*>(object);
|
|
||||||
assert(motionState);
|
|
||||||
assert(motionState->_avatar);
|
|
||||||
_avatarsToChangeInPhysics.insert(motionState->_avatar);
|
|
||||||
}
|
|
||||||
// things on objectsToRemove are ready for delete
|
// things on objectsToRemove are ready for delete
|
||||||
for (auto object : transaction.objectsToRemove) {
|
for (auto object : transaction.objectsToRemove) {
|
||||||
delete object;
|
delete object;
|
||||||
|
@ -570,7 +612,7 @@ void AvatarManager::clearOtherAvatars() {
|
||||||
++avatarIterator;
|
++avatarIterator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& av : removedAvatars) {
|
for (auto& av : removedAvatars) {
|
||||||
handleRemovedAvatar(av);
|
handleRemovedAvatar(av);
|
||||||
|
@ -578,7 +620,7 @@ void AvatarManager::clearOtherAvatars() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::deleteAllAvatars() {
|
void AvatarManager::deleteAllAvatars() {
|
||||||
assert(_avatarsToChangeInPhysics.empty());
|
assert(_otherAvatarsToChangeInPhysics.empty());
|
||||||
QReadLocker locker(&_hashLock);
|
QReadLocker locker(&_hashLock);
|
||||||
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||||
while (avatarIterator != _avatarHash.end()) {
|
while (avatarIterator != _avatarHash.end()) {
|
||||||
|
@ -588,7 +630,7 @@ void AvatarManager::deleteAllAvatars() {
|
||||||
if (avatar != _myAvatar) {
|
if (avatar != _myAvatar) {
|
||||||
auto otherAvatar = std::static_pointer_cast<OtherAvatar>(avatar);
|
auto otherAvatar = std::static_pointer_cast<OtherAvatar>(avatar);
|
||||||
assert(!otherAvatar->_motionState);
|
assert(!otherAvatar->_motionState);
|
||||||
assert(otherAvatar->getDetailedMotionStates().size() == 0);
|
assert(otherAvatar->getDetailedMotionStates().size() == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,6 +273,8 @@ public slots:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) override;
|
AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) override;
|
||||||
|
DetailedMotionState* createDetailedMotionState(OtherAvatarPointer avatar, int32_t jointIndex);
|
||||||
|
void rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction, OtherAvatarPointer avatar);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit AvatarManager(QObject* parent = 0);
|
explicit AvatarManager(QObject* parent = 0);
|
||||||
|
@ -288,7 +290,7 @@ private:
|
||||||
void handleTransitAnimations(AvatarTransit::Status status);
|
void handleTransitAnimations(AvatarTransit::Status status);
|
||||||
|
|
||||||
using SetOfOtherAvatars = std::set<OtherAvatarPointer>;
|
using SetOfOtherAvatars = std::set<OtherAvatarPointer>;
|
||||||
SetOfOtherAvatars _avatarsToChangeInPhysics;
|
SetOfOtherAvatars _otherAvatarsToChangeInPhysics;
|
||||||
|
|
||||||
std::shared_ptr<MyAvatar> _myAvatar;
|
std::shared_ptr<MyAvatar> _myAvatar;
|
||||||
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
|
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
|
||||||
|
|
|
@ -29,23 +29,19 @@ void AvatarMotionState::handleEasyChanges(uint32_t& flags) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AvatarMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
|
||||||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
AvatarMotionState::~AvatarMotionState() {
|
AvatarMotionState::~AvatarMotionState() {
|
||||||
assert(_avatar);
|
assert(_avatar);
|
||||||
_avatar = nullptr;
|
_avatar = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
uint32_t AvatarMotionState::getIncomingDirtyFlags() {
|
uint32_t AvatarMotionState::getIncomingDirtyFlags() const {
|
||||||
return _body ? _dirtyFlags : 0;
|
return _body ? _dirtyFlags : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMotionState::clearIncomingDirtyFlags() {
|
void AvatarMotionState::clearIncomingDirtyFlags(uint32_t mask) {
|
||||||
if (_body) {
|
if (_body) {
|
||||||
_dirtyFlags = 0;
|
_dirtyFlags &= ~mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,13 +50,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
|
||||||
return MOTION_TYPE_DYNAMIC;
|
return MOTION_TYPE_DYNAMIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual and protected
|
|
||||||
const btCollisionShape* AvatarMotionState::computeNewShape() {
|
|
||||||
ShapeInfo shapeInfo;
|
|
||||||
_avatar->computeShapeInfo(shapeInfo);
|
|
||||||
return getShapeManager()->getShape(shapeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
bool AvatarMotionState::isMoving() const {
|
bool AvatarMotionState::isMoving() const {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -23,44 +23,44 @@ class AvatarMotionState : public ObjectMotionState {
|
||||||
public:
|
public:
|
||||||
AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape);
|
AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape);
|
||||||
|
|
||||||
virtual void handleEasyChanges(uint32_t& flags) override;
|
void handleEasyChanges(uint32_t& flags) override;
|
||||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override;
|
|
||||||
|
|
||||||
virtual PhysicsMotionType getMotionType() const override { return _motionType; }
|
PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||||
|
|
||||||
virtual uint32_t getIncomingDirtyFlags() override;
|
uint32_t getIncomingDirtyFlags() const override;
|
||||||
virtual void clearIncomingDirtyFlags() override;
|
void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override;
|
||||||
|
|
||||||
virtual PhysicsMotionType computePhysicsMotionType() const override;
|
PhysicsMotionType computePhysicsMotionType() const override;
|
||||||
|
|
||||||
virtual bool isMoving() const override;
|
bool isMoving() const override;
|
||||||
|
|
||||||
// this relays incoming position/rotation to the RigidBody
|
// this relays incoming position/rotation to the RigidBody
|
||||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
void getWorldTransform(btTransform& worldTrans) const override;
|
||||||
|
|
||||||
// this relays outgoing position/rotation to the EntityItem
|
// this relays outgoing position/rotation to the EntityItem
|
||||||
virtual void setWorldTransform(const btTransform& worldTrans) override;
|
void setWorldTransform(const btTransform& worldTrans) override;
|
||||||
|
|
||||||
|
|
||||||
// These pure virtual methods must be implemented for each MotionState type
|
// These pure virtual methods must be implemented for each MotionState type
|
||||||
// and make it possible to implement more complicated methods in this base class.
|
// and make it possible to implement more complicated methods in this base class.
|
||||||
|
|
||||||
// pure virtual overrides from ObjectMotionState
|
// pure virtual overrides from ObjectMotionState
|
||||||
virtual float getObjectRestitution() const override;
|
float getObjectRestitution() const override;
|
||||||
virtual float getObjectFriction() const override;
|
float getObjectFriction() const override;
|
||||||
virtual float getObjectLinearDamping() const override;
|
float getObjectLinearDamping() const override;
|
||||||
virtual float getObjectAngularDamping() const override;
|
float getObjectAngularDamping() const override;
|
||||||
|
|
||||||
virtual glm::vec3 getObjectPosition() const override;
|
glm::vec3 getObjectPosition() const override;
|
||||||
virtual glm::quat getObjectRotation() const override;
|
glm::quat getObjectRotation() const override;
|
||||||
virtual glm::vec3 getObjectLinearVelocity() const override;
|
glm::vec3 getObjectLinearVelocity() const override;
|
||||||
virtual glm::vec3 getObjectAngularVelocity() const override;
|
glm::vec3 getObjectAngularVelocity() const override;
|
||||||
virtual glm::vec3 getObjectGravity() const override;
|
glm::vec3 getObjectGravity() const override;
|
||||||
|
|
||||||
virtual const QUuid getObjectID() const override;
|
const QUuid getObjectID() const override;
|
||||||
|
|
||||||
virtual QString getName() const override;
|
QString getName() const override;
|
||||||
virtual QUuid getSimulatorID() const override;
|
ShapeType getShapeType() const override { return SHAPE_TYPE_CAPSULE_Y; }
|
||||||
|
QUuid getSimulatorID() const override;
|
||||||
|
|
||||||
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);
|
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);
|
||||||
|
|
||||||
|
@ -69,9 +69,9 @@ public:
|
||||||
void setCollisionGroup(int32_t group) { _collisionGroup = group; }
|
void setCollisionGroup(int32_t group) { _collisionGroup = group; }
|
||||||
int32_t getCollisionGroup() { return _collisionGroup; }
|
int32_t getCollisionGroup() { return _collisionGroup; }
|
||||||
|
|
||||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||||
|
|
||||||
virtual float getMass() const override;
|
float getMass() const override;
|
||||||
|
|
||||||
friend class AvatarManager;
|
friend class AvatarManager;
|
||||||
friend class Avatar;
|
friend class Avatar;
|
||||||
|
@ -85,9 +85,6 @@ protected:
|
||||||
// ever called by the Avatar class dtor.
|
// ever called by the Avatar class dtor.
|
||||||
~AvatarMotionState();
|
~AvatarMotionState();
|
||||||
|
|
||||||
virtual bool isReadyToComputeShape() const override { return true; }
|
|
||||||
virtual const btCollisionShape* computeNewShape() override;
|
|
||||||
|
|
||||||
OtherAvatarPointer _avatar;
|
OtherAvatarPointer _avatar;
|
||||||
float _diameter { 0.0f };
|
float _diameter { 0.0f };
|
||||||
int32_t _collisionGroup;
|
int32_t _collisionGroup;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "MyAvatar.h"
|
#include "MyAvatar.h"
|
||||||
|
|
||||||
|
|
||||||
DetailedMotionState::DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex) :
|
DetailedMotionState::DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int32_t jointIndex) :
|
||||||
ObjectMotionState(shape), _avatar(avatar), _jointIndex(jointIndex) {
|
ObjectMotionState(shape), _avatar(avatar), _jointIndex(jointIndex) {
|
||||||
assert(_avatar);
|
assert(_avatar);
|
||||||
if (!_avatar->isMyAvatar()) {
|
if (!_avatar->isMyAvatar()) {
|
||||||
|
@ -33,47 +33,26 @@ void DetailedMotionState::handleEasyChanges(uint32_t& flags) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DetailedMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
|
||||||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
DetailedMotionState::~DetailedMotionState() {
|
DetailedMotionState::~DetailedMotionState() {
|
||||||
assert(_avatar);
|
assert(_avatar);
|
||||||
_avatar = nullptr;
|
_avatar = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
uint32_t DetailedMotionState::getIncomingDirtyFlags() {
|
uint32_t DetailedMotionState::getIncomingDirtyFlags() const {
|
||||||
return _body ? _dirtyFlags : 0;
|
return _body ? _dirtyFlags : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailedMotionState::clearIncomingDirtyFlags() {
|
void DetailedMotionState::clearIncomingDirtyFlags(uint32_t mask) {
|
||||||
if (_body) {
|
if (_body) {
|
||||||
_dirtyFlags = 0;
|
_dirtyFlags &= ~mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicsMotionType DetailedMotionState::computePhysicsMotionType() const {
|
PhysicsMotionType DetailedMotionState::computePhysicsMotionType() const {
|
||||||
// TODO?: support non-DYNAMIC motion for avatars? (e.g. when sitting)
|
|
||||||
return MOTION_TYPE_KINEMATIC;
|
return MOTION_TYPE_KINEMATIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual and protected
|
|
||||||
const btCollisionShape* DetailedMotionState::computeNewShape() {
|
|
||||||
btCollisionShape* shape = nullptr;
|
|
||||||
if (!_avatar->isMyAvatar()) {
|
|
||||||
if (_otherAvatar != nullptr) {
|
|
||||||
shape = _otherAvatar->createCollisionShape(_jointIndex, _isBound, _boundJoints);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::shared_ptr<MyAvatar> myAvatar = std::static_pointer_cast<MyAvatar>(_avatar);
|
|
||||||
if (myAvatar) {
|
|
||||||
shape = myAvatar->getCharacterController()->createDetailedCollisionShapeForJoint(_jointIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
bool DetailedMotionState::isMoving() const {
|
bool DetailedMotionState::isMoving() const {
|
||||||
return false;
|
return false;
|
||||||
|
@ -178,11 +157,23 @@ void DetailedMotionState::setRigidBody(btRigidBody* body) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailedMotionState::setShape(const btCollisionShape* shape) {
|
void DetailedMotionState::setShape(const btCollisionShape* shape) {
|
||||||
ObjectMotionState::setShape(shape);
|
if (_shape != shape) {
|
||||||
|
if (_shape) {
|
||||||
|
getShapeManager()->releaseShape(_shape);
|
||||||
|
}
|
||||||
|
_shape = shape;
|
||||||
|
if (_body) {
|
||||||
|
assert(_shape);
|
||||||
|
_body->setCollisionShape(const_cast<btCollisionShape*>(_shape));
|
||||||
|
}
|
||||||
|
} else if (shape) {
|
||||||
|
// we need to release unused reference to shape
|
||||||
|
getShapeManager()->releaseShape(shape);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DetailedMotionState::forceActive() {
|
void DetailedMotionState::forceActive() {
|
||||||
if (_body && !_body->isActive()) {
|
if (_body && !_body->isActive()) {
|
||||||
_body->setActivationState(ACTIVE_TAG);
|
_body->setActivationState(ACTIVE_TAG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,55 +23,55 @@ class DetailedMotionState : public ObjectMotionState {
|
||||||
public:
|
public:
|
||||||
DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex);
|
DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex);
|
||||||
|
|
||||||
virtual void handleEasyChanges(uint32_t& flags) override;
|
void handleEasyChanges(uint32_t& flags) override;
|
||||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override;
|
|
||||||
|
|
||||||
virtual PhysicsMotionType getMotionType() const override { return _motionType; }
|
PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||||
|
|
||||||
virtual uint32_t getIncomingDirtyFlags() override;
|
uint32_t getIncomingDirtyFlags() const override;
|
||||||
virtual void clearIncomingDirtyFlags() override;
|
void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override;
|
||||||
|
|
||||||
virtual PhysicsMotionType computePhysicsMotionType() const override;
|
PhysicsMotionType computePhysicsMotionType() const override;
|
||||||
|
|
||||||
virtual bool isMoving() const override;
|
bool isMoving() const override;
|
||||||
|
|
||||||
// this relays incoming position/rotation to the RigidBody
|
// this relays incoming position/rotation to the RigidBody
|
||||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
void getWorldTransform(btTransform& worldTrans) const override;
|
||||||
|
|
||||||
// this relays outgoing position/rotation to the EntityItem
|
// this relays outgoing position/rotation to the EntityItem
|
||||||
virtual void setWorldTransform(const btTransform& worldTrans) override;
|
void setWorldTransform(const btTransform& worldTrans) override;
|
||||||
|
|
||||||
|
|
||||||
// These pure virtual methods must be implemented for each MotionState type
|
// These pure virtual methods must be implemented for each MotionState type
|
||||||
// and make it possible to implement more complicated methods in this base class.
|
// and make it possible to implement more complicated methods in this base class.
|
||||||
|
|
||||||
// pure virtual overrides from ObjectMotionState
|
// pure virtual overrides from ObjectMotionState
|
||||||
virtual float getObjectRestitution() const override;
|
float getObjectRestitution() const override;
|
||||||
virtual float getObjectFriction() const override;
|
float getObjectFriction() const override;
|
||||||
virtual float getObjectLinearDamping() const override;
|
float getObjectLinearDamping() const override;
|
||||||
virtual float getObjectAngularDamping() const override;
|
float getObjectAngularDamping() const override;
|
||||||
|
|
||||||
virtual glm::vec3 getObjectPosition() const override;
|
glm::vec3 getObjectPosition() const override;
|
||||||
virtual glm::quat getObjectRotation() const override;
|
glm::quat getObjectRotation() const override;
|
||||||
virtual glm::vec3 getObjectLinearVelocity() const override;
|
glm::vec3 getObjectLinearVelocity() const override;
|
||||||
virtual glm::vec3 getObjectAngularVelocity() const override;
|
glm::vec3 getObjectAngularVelocity() const override;
|
||||||
virtual glm::vec3 getObjectGravity() const override;
|
glm::vec3 getObjectGravity() const override;
|
||||||
|
|
||||||
virtual const QUuid getObjectID() const override;
|
const QUuid getObjectID() const override;
|
||||||
|
|
||||||
virtual QString getName() const override;
|
QString getName() const override;
|
||||||
virtual QUuid getSimulatorID() const override;
|
ShapeType getShapeType() const override { return SHAPE_TYPE_HULL; }
|
||||||
|
QUuid getSimulatorID() const override;
|
||||||
|
|
||||||
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
|
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
|
||||||
|
|
||||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||||
|
|
||||||
virtual float getMass() const override;
|
float getMass() const override;
|
||||||
void forceActive();
|
void forceActive();
|
||||||
QUuid getAvatarID() const { return _avatar->getID(); }
|
QUuid getAvatarID() const { return _avatar->getID(); }
|
||||||
int getJointIndex() const { return _jointIndex; }
|
int32_t getJointIndex() const { return _jointIndex; }
|
||||||
void setIsBound(bool isBound, std::vector<int> boundJoints) { _isBound = isBound; _boundJoints = boundJoints; }
|
void setIsBound(bool isBound, const std::vector<int32_t>& boundJoints) { _isBound = isBound; _boundJoints = boundJoints; }
|
||||||
bool getIsBound(std::vector<int>& boundJoints) const { boundJoints = _boundJoints; return _isBound; }
|
bool getIsBound(std::vector<int32_t>& boundJoints) const { boundJoints = _boundJoints; return _isBound; }
|
||||||
|
|
||||||
friend class AvatarManager;
|
friend class AvatarManager;
|
||||||
friend class Avatar;
|
friend class Avatar;
|
||||||
|
@ -84,17 +84,14 @@ protected:
|
||||||
// ever called by the Avatar class dtor.
|
// ever called by the Avatar class dtor.
|
||||||
~DetailedMotionState();
|
~DetailedMotionState();
|
||||||
|
|
||||||
virtual bool isReadyToComputeShape() const override { return true; }
|
|
||||||
virtual const btCollisionShape* computeNewShape() override;
|
|
||||||
|
|
||||||
AvatarPointer _avatar;
|
AvatarPointer _avatar;
|
||||||
float _diameter { 0.0f };
|
float _diameter { 0.0f };
|
||||||
|
|
||||||
uint32_t _dirtyFlags;
|
uint32_t _dirtyFlags;
|
||||||
int _jointIndex { -1 };
|
int32_t _jointIndex { -1 };
|
||||||
OtherAvatarPointer _otherAvatar { nullptr };
|
OtherAvatarPointer _otherAvatar { nullptr };
|
||||||
bool _isBound { false };
|
bool _isBound { false };
|
||||||
std::vector<int> _boundJoints;
|
std::vector<int32_t> _boundJoints;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_DetailedMotionState_h
|
#endif // hifi_DetailedMotionState_h
|
||||||
|
|
|
@ -377,21 +377,18 @@ void MyCharacterController::updateMassProperties() {
|
||||||
_rigidBody->setMassProps(mass, inertia);
|
_rigidBody->setMassProps(mass, inertia);
|
||||||
}
|
}
|
||||||
|
|
||||||
btCollisionShape* MyCharacterController::createDetailedCollisionShapeForJoint(int jointIndex) {
|
const btCollisionShape* MyCharacterController::createDetailedCollisionShapeForJoint(int32_t jointIndex) {
|
||||||
ShapeInfo shapeInfo;
|
ShapeInfo shapeInfo;
|
||||||
_avatar->computeDetailedShapeInfo(shapeInfo, jointIndex);
|
_avatar->computeDetailedShapeInfo(shapeInfo, jointIndex);
|
||||||
if (shapeInfo.getType() != SHAPE_TYPE_NONE) {
|
if (shapeInfo.getType() != SHAPE_TYPE_NONE) {
|
||||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
const btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||||
if (shape) {
|
|
||||||
shape->setMargin(0.001f);
|
|
||||||
}
|
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
DetailedMotionState* MyCharacterController::createDetailedMotionStateForJoint(int jointIndex) {
|
DetailedMotionState* MyCharacterController::createDetailedMotionStateForJoint(int32_t jointIndex) {
|
||||||
auto shape = createDetailedCollisionShapeForJoint(jointIndex);
|
const btCollisionShape* shape = createDetailedCollisionShapeForJoint(jointIndex);
|
||||||
if (shape) {
|
if (shape) {
|
||||||
DetailedMotionState* motionState = new DetailedMotionState(_avatar, shape, jointIndex);
|
DetailedMotionState* motionState = new DetailedMotionState(_avatar, shape, jointIndex);
|
||||||
motionState->setMass(_avatar->computeMass());
|
motionState->setMass(_avatar->computeMass());
|
||||||
|
@ -423,25 +420,16 @@ void MyCharacterController::buildPhysicsTransaction(PhysicsEngine::Transaction&
|
||||||
}
|
}
|
||||||
if (_pendingFlags & PENDING_FLAG_ADD_DETAILED_TO_SIMULATION) {
|
if (_pendingFlags & PENDING_FLAG_ADD_DETAILED_TO_SIMULATION) {
|
||||||
_pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION;
|
_pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION;
|
||||||
for (int i = 0; i < _avatar->getJointCount(); i++) {
|
for (int32_t i = 0; i < _avatar->getJointCount(); i++) {
|
||||||
auto dMotionState = createDetailedMotionStateForJoint(i);
|
auto dMotionState = createDetailedMotionStateForJoint(i);
|
||||||
if (dMotionState) {
|
if (dMotionState) {
|
||||||
_detailedMotionStates.push_back(dMotionState);
|
_detailedMotionStates.push_back(dMotionState);
|
||||||
transaction.objectsToAdd.push_back(dMotionState);
|
transaction.objectsToAdd.push_back(dMotionState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MyCharacterController::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
|
||||||
// things on objectsToRemove are ready for delete
|
|
||||||
for (auto object : transaction.objectsToRemove) {
|
|
||||||
delete object;
|
|
||||||
}
|
}
|
||||||
transaction.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DetailedRayResultCallback : public btCollisionWorld::AllHitsRayResultCallback {
|
class DetailedRayResultCallback : public btCollisionWorld::AllHitsRayResultCallback {
|
||||||
public:
|
public:
|
||||||
DetailedRayResultCallback()
|
DetailedRayResultCallback()
|
||||||
|
@ -467,7 +455,7 @@ std::vector<MyCharacterController::RayAvatarResult> MyCharacterController::rayTe
|
||||||
_dynamicsWorld->rayTest(origin, end, rayCallback);
|
_dynamicsWorld->rayTest(origin, end, rayCallback);
|
||||||
if (rayCallback.m_hitFractions.size() > 0) {
|
if (rayCallback.m_hitFractions.size() > 0) {
|
||||||
foundAvatars.reserve(rayCallback.m_hitFractions.size());
|
foundAvatars.reserve(rayCallback.m_hitFractions.size());
|
||||||
for (int i = 0; i < rayCallback.m_hitFractions.size(); i++) {
|
for (int32_t i = 0; i < rayCallback.m_hitFractions.size(); i++) {
|
||||||
auto object = rayCallback.m_collisionObjects[i];
|
auto object = rayCallback.m_collisionObjects[i];
|
||||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(object->getUserPointer());
|
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(object->getUserPointer());
|
||||||
if (motionState && motionState->getType() == MOTIONSTATE_TYPE_DETAILED) {
|
if (motionState && motionState->getType() == MOTIONSTATE_TYPE_DETAILED) {
|
||||||
|
@ -493,4 +481,4 @@ std::vector<MyCharacterController::RayAvatarResult> MyCharacterController::rayTe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return foundAvatars;
|
return foundAvatars;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,27 +44,25 @@ public:
|
||||||
|
|
||||||
void setDensity(btScalar density) { _density = density; }
|
void setDensity(btScalar density) { _density = density; }
|
||||||
|
|
||||||
btCollisionShape* createDetailedCollisionShapeForJoint(int jointIndex);
|
const btCollisionShape* createDetailedCollisionShapeForJoint(int32_t jointIndex);
|
||||||
DetailedMotionState* createDetailedMotionStateForJoint(int jointIndex);
|
DetailedMotionState* createDetailedMotionStateForJoint(int32_t jointIndex);
|
||||||
std::vector<DetailedMotionState*>& getDetailedMotionStates() { return _detailedMotionStates; }
|
std::vector<DetailedMotionState*>& getDetailedMotionStates() { return _detailedMotionStates; }
|
||||||
void clearDetailedMotionStates();
|
void clearDetailedMotionStates();
|
||||||
void resetDetailedMotionStates();
|
void resetDetailedMotionStates();
|
||||||
|
|
||||||
void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||||
void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
|
||||||
|
|
||||||
|
|
||||||
struct RayAvatarResult {
|
struct RayAvatarResult {
|
||||||
bool _intersect { false };
|
bool _intersect { false };
|
||||||
bool _isBound { false };
|
bool _isBound { false };
|
||||||
QUuid _intersectWithAvatar;
|
QUuid _intersectWithAvatar;
|
||||||
int _intersectWithJoint { -1 };
|
int32_t _intersectWithJoint { -1 };
|
||||||
float _distance { 0.0f };
|
float _distance { 0.0f };
|
||||||
float _maxDistance { 0.0f };
|
float _maxDistance { 0.0f };
|
||||||
QVariantMap _extraInfo;
|
QVariantMap _extraInfo;
|
||||||
glm::vec3 _intersectionPoint;
|
glm::vec3 _intersectionPoint;
|
||||||
glm::vec3 _intersectionNormal;
|
glm::vec3 _intersectionNormal;
|
||||||
std::vector<int> _boundJoints;
|
std::vector<int32_t> _boundJoints;
|
||||||
};
|
};
|
||||||
std::vector<RayAvatarResult> rayTest(const btVector3& origin, const btVector3& direction, const btScalar& length,
|
std::vector<RayAvatarResult> rayTest(const btVector3& origin, const btVector3& direction, const btScalar& length,
|
||||||
const QVector<uint>& jointsToExclude) const;
|
const QVector<uint>& jointsToExclude) const;
|
||||||
|
|
|
@ -116,6 +116,8 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const {
|
||||||
int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) {
|
int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
int32_t bytesRead = Avatar::parseDataFromBuffer(buffer);
|
int32_t bytesRead = Avatar::parseDataFromBuffer(buffer);
|
||||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||||
|
// NOTE: we activate _detailedMotionStates is because they are KINEMATIC
|
||||||
|
// and Bullet will automagically call DetailedMotionState::getWorldTransform() when active.
|
||||||
_detailedMotionStates[i]->forceActive();
|
_detailedMotionStates[i]->forceActive();
|
||||||
}
|
}
|
||||||
if (_moving && _motionState) {
|
if (_moving && _motionState) {
|
||||||
|
@ -124,11 +126,11 @@ int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
btCollisionShape* OtherAvatar::createCollisionShape(int jointIndex, bool& isBound, std::vector<int>& boundJoints) {
|
const btCollisionShape* OtherAvatar::createCollisionShape(int32_t jointIndex, bool& isBound, std::vector<int32_t>& boundJoints) {
|
||||||
ShapeInfo shapeInfo;
|
ShapeInfo shapeInfo;
|
||||||
isBound = false;
|
isBound = false;
|
||||||
QString jointName = "";
|
QString jointName = "";
|
||||||
if (jointIndex > -1 && jointIndex < (int)_multiSphereShapes.size()) {
|
if (jointIndex > -1 && jointIndex < (int32_t)_multiSphereShapes.size()) {
|
||||||
jointName = _multiSphereShapes[jointIndex].getJointName();
|
jointName = _multiSphereShapes[jointIndex].getJointName();
|
||||||
}
|
}
|
||||||
switch (_bodyLOD) {
|
switch (_bodyLOD) {
|
||||||
|
@ -163,39 +165,21 @@ btCollisionShape* OtherAvatar::createCollisionShape(int jointIndex, bool& isBoun
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Note: MultiSphereLow case really means: "skip fingers and use spheres for hands,
|
||||||
|
// else fall through to MultiSphereHigh case"
|
||||||
case BodyLOD::MultiSphereHigh:
|
case BodyLOD::MultiSphereHigh:
|
||||||
computeDetailedShapeInfo(shapeInfo, jointIndex);
|
computeDetailedShapeInfo(shapeInfo, jointIndex);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
assert(false); // should never reach here
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (shapeInfo.getType() != SHAPE_TYPE_NONE) {
|
return ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||||
auto shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
|
||||||
if (shape) {
|
|
||||||
shape->setMargin(0.001f);
|
|
||||||
}
|
|
||||||
return shape;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
DetailedMotionState* OtherAvatar::createMotionState(std::shared_ptr<OtherAvatar> avatar, int jointIndex) {
|
|
||||||
bool isBound = false;
|
|
||||||
std::vector<int> boundJoints;
|
|
||||||
btCollisionShape* shape = createCollisionShape(jointIndex, isBound, boundJoints);
|
|
||||||
if (shape) {
|
|
||||||
DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex);
|
|
||||||
motionState->setMass(computeMass());
|
|
||||||
motionState->setIsBound(isBound, boundJoints);
|
|
||||||
return motionState;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OtherAvatar::resetDetailedMotionStates() {
|
void OtherAvatar::resetDetailedMotionStates() {
|
||||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
// NOTE: the DetailedMotionStates are deleted after being added to PhysicsEngine::Transaction::_objectsToRemove
|
||||||
_detailedMotionStates[i] = nullptr;
|
// See AvatarManager::handleProcessedPhysicsTransaction()
|
||||||
}
|
|
||||||
_detailedMotionStates.clear();
|
_detailedMotionStates.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,11 +215,11 @@ void OtherAvatar::computeShapeLOD() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OtherAvatar::isInPhysicsSimulation() const {
|
bool OtherAvatar::isInPhysicsSimulation() const {
|
||||||
return _motionState != nullptr && _detailedMotionStates.size() > 0;
|
return _motionState && _motionState->getRigidBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OtherAvatar::shouldBeInPhysicsSimulation() const {
|
bool OtherAvatar::shouldBeInPhysicsSimulation() const {
|
||||||
return !isDead() && !(isInPhysicsSimulation() && _needsReinsertion);
|
return !isDead() && _workloadRegion < workload::Region::R3;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OtherAvatar::needsPhysicsUpdate() const {
|
bool OtherAvatar::needsPhysicsUpdate() const {
|
||||||
|
@ -245,12 +229,9 @@ bool OtherAvatar::needsPhysicsUpdate() const {
|
||||||
|
|
||||||
void OtherAvatar::rebuildCollisionShape() {
|
void OtherAvatar::rebuildCollisionShape() {
|
||||||
if (_motionState) {
|
if (_motionState) {
|
||||||
|
// do not actually rebuild here, instead flag for later
|
||||||
_motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
_motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||||
}
|
_needsReinsertion = true;
|
||||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
|
||||||
if (_detailedMotionStates[i]) {
|
|
||||||
_detailedMotionStates[i]->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,25 +241,6 @@ void OtherAvatar::setCollisionWithOtherAvatarsFlags() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OtherAvatar::createDetailedMotionStates(const std::shared_ptr<OtherAvatar>& avatar) {
|
|
||||||
auto& detailedMotionStates = getDetailedMotionStates();
|
|
||||||
assert(detailedMotionStates.empty());
|
|
||||||
if (_bodyLOD == BodyLOD::Sphere) {
|
|
||||||
auto dMotionState = createMotionState(avatar, -1);
|
|
||||||
if (dMotionState) {
|
|
||||||
detailedMotionStates.push_back(dMotionState);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < getJointCount(); i++) {
|
|
||||||
auto dMotionState = createMotionState(avatar, i);
|
|
||||||
if (dMotionState) {
|
|
||||||
detailedMotionStates.push_back(dMotionState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_needsReinsertion = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OtherAvatar::simulate(float deltaTime, bool inView) {
|
void OtherAvatar::simulate(float deltaTime, bool inView) {
|
||||||
PROFILE_RANGE(simulation, "simulate");
|
PROFILE_RANGE(simulation, "simulate");
|
||||||
|
|
||||||
|
|
|
@ -52,9 +52,7 @@ public:
|
||||||
bool shouldBeInPhysicsSimulation() const;
|
bool shouldBeInPhysicsSimulation() const;
|
||||||
bool needsPhysicsUpdate() const;
|
bool needsPhysicsUpdate() const;
|
||||||
|
|
||||||
btCollisionShape* createCollisionShape(int jointIndex, bool& isBound, std::vector<int>& boundJoints);
|
const btCollisionShape* createCollisionShape(int32_t jointIndex, bool& isBound, std::vector<int32_t>& boundJoints);
|
||||||
DetailedMotionState* createMotionState(std::shared_ptr<OtherAvatar> avatar, int jointIndex);
|
|
||||||
void createDetailedMotionStates(const std::shared_ptr<OtherAvatar>& avatar);
|
|
||||||
std::vector<DetailedMotionState*>& getDetailedMotionStates() { return _detailedMotionStates; }
|
std::vector<DetailedMotionState*>& getDetailedMotionStates() { return _detailedMotionStates; }
|
||||||
void resetDetailedMotionStates();
|
void resetDetailedMotionStates();
|
||||||
BodyLOD getBodyLOD() { return _bodyLOD; }
|
BodyLOD getBodyLOD() { return _bodyLOD; }
|
||||||
|
|
|
@ -168,7 +168,7 @@ bool isEntityPhysicsReady(const EntityItemPointer& entity) {
|
||||||
bool hasAABox;
|
bool hasAABox;
|
||||||
entity->getAABox(hasAABox);
|
entity->getAABox(hasAABox);
|
||||||
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
|
if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) {
|
||||||
return (!entity->shouldBePhysical() || entity->isReadyToComputeShape() || modelEntity->computeShapeFailedToLoad());
|
return (!entity->shouldBePhysical() || entity->isInPhysicsSimulation() || modelEntity->computeShapeFailedToLoad());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -481,6 +481,12 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityTreeRenderer::preUpdate() {
|
||||||
|
if (_tree && !_shuttingDown) {
|
||||||
|
_tree->preUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::update(bool simulate) {
|
void EntityTreeRenderer::update(bool simulate) {
|
||||||
PROFILE_RANGE(simulation_physics, "ETR::update");
|
PROFILE_RANGE(simulation_physics, "ETR::update");
|
||||||
PerformanceTimer perfTimer("ETRupdate");
|
PerformanceTimer perfTimer("ETRupdate");
|
||||||
|
|
|
@ -78,6 +78,7 @@ public:
|
||||||
void setSetPrecisionPickingOperator(std::function<void(unsigned int, bool)> setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; }
|
void setSetPrecisionPickingOperator(std::function<void(unsigned int, bool)> setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; }
|
||||||
|
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
void preUpdate();
|
||||||
void update(bool simulate);
|
void update(bool simulate);
|
||||||
|
|
||||||
EntityTreePointer getTree() { return std::static_pointer_cast<EntityTree>(_tree); }
|
EntityTreePointer getTree() { return std::static_pointer_cast<EntityTree>(_tree); }
|
||||||
|
|
|
@ -735,13 +735,15 @@ bool RenderableModelEntityItem::shouldBePhysical() const {
|
||||||
auto model = getModel();
|
auto model = getModel();
|
||||||
// If we have a model, make sure it hasn't failed to download.
|
// If we have a model, make sure it hasn't failed to download.
|
||||||
// If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready.
|
// If it has, we'll report back that we shouldn't be physical so that physics aren't held waiting for us to be ready.
|
||||||
if (model && (getShapeType() == SHAPE_TYPE_COMPOUND || getShapeType() == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) {
|
ShapeType shapeType = getShapeType();
|
||||||
return false;
|
if (model) {
|
||||||
} else if (model && getShapeType() != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) {
|
if ((shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) && model->didCollisionGeometryRequestFail()) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else if (shapeType != SHAPE_TYPE_NONE && model->didVisualGeometryRequestFail()) {
|
||||||
return ModelEntityItem::shouldBePhysical();
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return !isDead() && shapeType != SHAPE_TYPE_NONE && QUrl(_modelURL).isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
int RenderableModelEntityItem::getJointParent(int index) const {
|
int RenderableModelEntityItem::getJointParent(int index) const {
|
||||||
|
|
|
@ -72,7 +72,6 @@ public:
|
||||||
glm::mat4 localToVoxelMatrix() const;
|
glm::mat4 localToVoxelMatrix() const;
|
||||||
|
|
||||||
virtual ShapeType getShapeType() const override;
|
virtual ShapeType getShapeType() const override;
|
||||||
virtual bool shouldBePhysical() const override { return !isDead(); }
|
|
||||||
virtual bool isReadyToComputeShape() const override;
|
virtual bool isReadyToComputeShape() const override;
|
||||||
virtual void computeShapeInfo(ShapeInfo& info) override;
|
virtual void computeShapeInfo(ShapeInfo& info) override;
|
||||||
|
|
||||||
|
|
|
@ -1828,42 +1828,42 @@ void EntityItem::setParentID(const QUuid& value) {
|
||||||
if (!value.isNull() && tree) {
|
if (!value.isNull() && tree) {
|
||||||
EntityItemPointer entity = tree->findEntityByEntityItemID(value);
|
EntityItemPointer entity = tree->findEntityByEntityItemID(value);
|
||||||
if (entity) {
|
if (entity) {
|
||||||
newParentNoBootstrapping = entity->getSpecialFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
newParentNoBootstrapping = entity->getSpecialFlags() & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!oldParentID.isNull() && tree) {
|
if (!oldParentID.isNull() && tree) {
|
||||||
EntityItemPointer entity = tree->findEntityByEntityItemID(oldParentID);
|
EntityItemPointer entity = tree->findEntityByEntityItemID(oldParentID);
|
||||||
if (entity) {
|
if (entity) {
|
||||||
oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
oldParentNoBootstrapping = entity->getDirtyFlags() & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!value.isNull() && (value == Physics::getSessionUUID() || value == AVATAR_SELF_ID)) {
|
if (!value.isNull() && (value == Physics::getSessionUUID() || value == AVATAR_SELF_ID)) {
|
||||||
newParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
newParentNoBootstrapping |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!oldParentID.isNull() && (oldParentID == Physics::getSessionUUID() || oldParentID == AVATAR_SELF_ID)) {
|
if (!oldParentID.isNull() && (oldParentID == Physics::getSessionUUID() || oldParentID == AVATAR_SELF_ID)) {
|
||||||
oldParentNoBootstrapping |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
oldParentNoBootstrapping |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) {
|
if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) {
|
||||||
if ((bool)(newParentNoBootstrapping & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
|
if ((bool)(newParentNoBootstrapping & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) {
|
||||||
markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||||
forEachDescendant([&](SpatiallyNestablePointer object) {
|
forEachDescendant([&](SpatiallyNestablePointer object) {
|
||||||
if (object->getNestableType() == NestableType::Entity) {
|
if (object->getNestableType() == NestableType::Entity) {
|
||||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
|
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
|
||||||
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||||
entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
entity->markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||||
forEachDescendant([&](SpatiallyNestablePointer object) {
|
forEachDescendant([&](SpatiallyNestablePointer object) {
|
||||||
if (object->getNestableType() == NestableType::Entity) {
|
if (object->getNestableType() == NestableType::Entity) {
|
||||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
|
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object);
|
||||||
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||||
entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2102,7 +2102,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
|
if ((bool)(_flags & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) {
|
||||||
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
|
userMask &= ~USER_COLLISION_GROUP_MY_AVATAR;
|
||||||
}
|
}
|
||||||
mask = Physics::getDefaultCollisionMask(group) & (int32_t)(userMask);
|
mask = Physics::getDefaultCollisionMask(group) & (int32_t)(userMask);
|
||||||
|
@ -2173,8 +2173,8 @@ bool EntityItem::addAction(EntitySimulationPointer simulation, EntityDynamicPoin
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::enableNoBootstrap() {
|
void EntityItem::enableNoBootstrap() {
|
||||||
if (!(bool)(_flags & Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING)) {
|
if (!(bool)(_flags & Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING)) {
|
||||||
_flags |= Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
_flags |= Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||||
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
||||||
|
|
||||||
// NOTE: unlike disableNoBootstrap() below, we do not call simulation->changeEntity() here
|
// NOTE: unlike disableNoBootstrap() below, we do not call simulation->changeEntity() here
|
||||||
|
@ -2186,7 +2186,7 @@ void EntityItem::enableNoBootstrap() {
|
||||||
if (child->getNestableType() == NestableType::Entity) {
|
if (child->getNestableType() == NestableType::Entity) {
|
||||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
|
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
|
||||||
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||||
entity->markSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
entity->markSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2194,7 +2194,7 @@ void EntityItem::enableNoBootstrap() {
|
||||||
|
|
||||||
void EntityItem::disableNoBootstrap() {
|
void EntityItem::disableNoBootstrap() {
|
||||||
if (!stillHasMyGrabAction()) {
|
if (!stillHasMyGrabAction()) {
|
||||||
_flags &= ~Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
_flags &= ~Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING;
|
||||||
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
_flags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar
|
||||||
|
|
||||||
EntityTreePointer entityTree = getTree();
|
EntityTreePointer entityTree = getTree();
|
||||||
|
@ -2207,7 +2207,7 @@ void EntityItem::disableNoBootstrap() {
|
||||||
if (child->getNestableType() == NestableType::Entity) {
|
if (child->getNestableType() == NestableType::Entity) {
|
||||||
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
|
EntityItemPointer entity = std::static_pointer_cast<EntityItem>(child);
|
||||||
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
entity->markDirtyFlags(Simulation::DIRTY_COLLISION_GROUP);
|
||||||
entity->clearSpecialFlags(Simulation::SPECIAL_FLAGS_NO_BOOTSTRAPPING);
|
entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_NO_BOOTSTRAPPING);
|
||||||
simulation->changeEntity(entity);
|
simulation->changeEntity(entity);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2326,7 +2326,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi
|
||||||
if (removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) {
|
if (removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) {
|
||||||
disableNoBootstrap();
|
disableNoBootstrap();
|
||||||
} else {
|
} else {
|
||||||
// NO-OP: we assume SPECIAL_FLAGS_NO_BOOTSTRAPPING bits and collision group are correct
|
// NO-OP: we assume SPECIAL_FLAG_NO_BOOTSTRAPPING bits and collision group are correct
|
||||||
// because they should have been set correctly when the action was added
|
// because they should have been set correctly when the action was added
|
||||||
// and/or when children were linked
|
// and/or when children were linked
|
||||||
}
|
}
|
||||||
|
@ -3154,21 +3154,21 @@ DEFINE_PROPERTY_ACCESSOR(quint32, StaticCertificateVersion, staticCertificateVer
|
||||||
uint32_t EntityItem::getDirtyFlags() const {
|
uint32_t EntityItem::getDirtyFlags() const {
|
||||||
uint32_t result;
|
uint32_t result;
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
result = _flags & Simulation::DIRTY_FLAGS;
|
result = _flags & Simulation::DIRTY_FLAGS_MASK;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::markDirtyFlags(uint32_t mask) {
|
void EntityItem::markDirtyFlags(uint32_t mask) {
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
mask &= Simulation::DIRTY_FLAGS;
|
mask &= Simulation::DIRTY_FLAGS_MASK;
|
||||||
_flags |= mask;
|
_flags |= mask;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::clearDirtyFlags(uint32_t mask) {
|
void EntityItem::clearDirtyFlags(uint32_t mask) {
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
mask &= Simulation::DIRTY_FLAGS;
|
mask &= Simulation::DIRTY_FLAGS_MASK;
|
||||||
_flags &= ~mask;
|
_flags &= ~mask;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3176,21 +3176,21 @@ void EntityItem::clearDirtyFlags(uint32_t mask) {
|
||||||
uint32_t EntityItem::getSpecialFlags() const {
|
uint32_t EntityItem::getSpecialFlags() const {
|
||||||
uint32_t result;
|
uint32_t result;
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
result = _flags & Simulation::SPECIAL_FLAGS;
|
result = _flags & Simulation::SPECIAL_FLAGS_MASK;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::markSpecialFlags(uint32_t mask) {
|
void EntityItem::markSpecialFlags(uint32_t mask) {
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
mask &= Simulation::SPECIAL_FLAGS;
|
mask &= Simulation::SPECIAL_FLAGS_MASK;
|
||||||
_flags |= mask;
|
_flags |= mask;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::clearSpecialFlags(uint32_t mask) {
|
void EntityItem::clearSpecialFlags(uint32_t mask) {
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
mask &= Simulation::SPECIAL_FLAGS;
|
mask &= Simulation::SPECIAL_FLAGS_MASK;
|
||||||
_flags &= ~mask;
|
_flags &= ~mask;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <OctreeElement.h> // for OctreeElement::AppendState
|
#include <OctreeElement.h> // for OctreeElement::AppendState
|
||||||
#include <OctreePacketData.h>
|
#include <OctreePacketData.h>
|
||||||
#include <PhysicsCollisionGroups.h>
|
#include <PhysicsCollisionGroups.h>
|
||||||
|
#include <SimulationFlags.h>
|
||||||
#include <ShapeInfo.h>
|
#include <ShapeInfo.h>
|
||||||
#include <Transform.h>
|
#include <Transform.h>
|
||||||
#include <SpatiallyNestable.h>
|
#include <SpatiallyNestable.h>
|
||||||
|
@ -33,11 +34,11 @@
|
||||||
#include "EntityPropertyFlags.h"
|
#include "EntityPropertyFlags.h"
|
||||||
#include "EntityTypes.h"
|
#include "EntityTypes.h"
|
||||||
#include "SimulationOwner.h"
|
#include "SimulationOwner.h"
|
||||||
#include "SimulationFlags.h"
|
|
||||||
#include "EntityDynamicInterface.h"
|
#include "EntityDynamicInterface.h"
|
||||||
#include "GrabPropertyGroup.h"
|
#include "GrabPropertyGroup.h"
|
||||||
|
|
||||||
class EntitySimulation;
|
class EntitySimulation;
|
||||||
|
using EntitySimulationPointer = std::shared_ptr<EntitySimulation>;
|
||||||
class EntityTreeElement;
|
class EntityTreeElement;
|
||||||
class EntityTreeElementExtraEncodeData;
|
class EntityTreeElementExtraEncodeData;
|
||||||
class EntityDynamicInterface;
|
class EntityDynamicInterface;
|
||||||
|
@ -322,7 +323,7 @@ public:
|
||||||
bool getDynamic() const;
|
bool getDynamic() const;
|
||||||
void setDynamic(bool value);
|
void setDynamic(bool value);
|
||||||
|
|
||||||
virtual bool shouldBePhysical() const { return false; }
|
virtual bool shouldBePhysical() const { return !isDead() && getShapeType() != SHAPE_TYPE_NONE; }
|
||||||
bool isVisuallyReady() const { return _visuallyReady; }
|
bool isVisuallyReady() const { return _visuallyReady; }
|
||||||
|
|
||||||
bool getLocked() const;
|
bool getLocked() const;
|
||||||
|
@ -423,8 +424,9 @@ public:
|
||||||
|
|
||||||
bool isSimulated() const { return _simulated; }
|
bool isSimulated() const { return _simulated; }
|
||||||
|
|
||||||
void* getPhysicsInfo() const { return _physicsInfo; }
|
bool isInPhysicsSimulation() const { return (bool)(_flags & Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION); }
|
||||||
|
|
||||||
|
void* getPhysicsInfo() const { return _physicsInfo; }
|
||||||
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
||||||
|
|
||||||
EntityTreeElementPointer getElement() const { return _element; }
|
EntityTreeElementPointer getElement() const { return _element; }
|
||||||
|
|
|
@ -176,47 +176,44 @@ void EntitySimulation::addEntity(EntityItemPointer entity) {
|
||||||
void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
assert(entity);
|
assert(entity);
|
||||||
if (!entity->isSimulated()) {
|
_changedEntities.insert(entity);
|
||||||
// This entity was either never added to the simulation or has been removed
|
}
|
||||||
// (probably for pending delete), so we don't want to keep a pointer to it
|
|
||||||
// on any internal lists.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Although it is not the responsibility of the EntitySimulation to sort the tree for EXTERNAL changes
|
void EntitySimulation::processChangedEntities() {
|
||||||
// it IS responsibile for triggering deletes for entities that leave the bounds of the domain, hence
|
QMutexLocker lock(&_mutex);
|
||||||
// we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag.
|
PROFILE_RANGE_EX(simulation_physics, "processChangedEntities", 0xffff00ff, (uint64_t)_changedEntities.size());
|
||||||
|
for (auto& entity : _changedEntities) {
|
||||||
|
if (entity->isSimulated()) {
|
||||||
|
processChangedEntity(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_changedEntities.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntitySimulation::processChangedEntity(const EntityItemPointer& entity) {
|
||||||
uint32_t dirtyFlags = entity->getDirtyFlags();
|
uint32_t dirtyFlags = entity->getDirtyFlags();
|
||||||
if (dirtyFlags & Simulation::DIRTY_POSITION) {
|
|
||||||
AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE);
|
|
||||||
bool success;
|
|
||||||
AACube newCube = entity->getQueryAACube(success);
|
|
||||||
if (success && !domainBounds.touches(newCube)) {
|
|
||||||
qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
|
|
||||||
entity->die();
|
|
||||||
prepareEntityForDelete(entity);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dirtyFlags & Simulation::DIRTY_LIFETIME) {
|
if (dirtyFlags & (Simulation::DIRTY_LIFETIME | Simulation::DIRTY_UPDATEABLE)) {
|
||||||
if (entity->isMortal()) {
|
if (dirtyFlags & Simulation::DIRTY_LIFETIME) {
|
||||||
_mortalEntities.insert(entity);
|
if (entity->isMortal()) {
|
||||||
uint64_t expiry = entity->getExpiry();
|
_mortalEntities.insert(entity);
|
||||||
if (expiry < _nextExpiry) {
|
uint64_t expiry = entity->getExpiry();
|
||||||
_nextExpiry = expiry;
|
if (expiry < _nextExpiry) {
|
||||||
|
_nextExpiry = expiry;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_mortalEntities.remove(entity);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
_mortalEntities.remove(entity);
|
|
||||||
}
|
}
|
||||||
entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME);
|
if (dirtyFlags & Simulation::DIRTY_UPDATEABLE) {
|
||||||
|
if (entity->needsToCallUpdate()) {
|
||||||
|
_entitiesToUpdate.insert(entity);
|
||||||
|
} else {
|
||||||
|
_entitiesToUpdate.remove(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME | Simulation::DIRTY_UPDATEABLE);
|
||||||
}
|
}
|
||||||
if (entity->needsToCallUpdate()) {
|
|
||||||
_entitiesToUpdate.insert(entity);
|
|
||||||
} else {
|
|
||||||
_entitiesToUpdate.remove(entity);
|
|
||||||
}
|
|
||||||
changeEntityInternal(entity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::clearEntities() {
|
void EntitySimulation::clearEntities() {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#define hifi_EntitySimulation_h
|
#define hifi_EntitySimulation_h
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
@ -82,13 +83,15 @@ public:
|
||||||
/// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others.
|
/// \param entity pointer to EntityItem that needs to be put on the entitiesToDelete list and removed from others.
|
||||||
virtual void prepareEntityForDelete(EntityItemPointer entity);
|
virtual void prepareEntityForDelete(EntityItemPointer entity);
|
||||||
|
|
||||||
|
void processChangedEntities();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// These pure virtual methods are protected because they are not to be called will-nilly. The base class
|
// These pure virtual methods are protected because they are not to be called will-nilly. The base class
|
||||||
// calls them in the right places.
|
// calls them in the right places.
|
||||||
virtual void updateEntitiesInternal(uint64_t now) = 0;
|
virtual void updateEntitiesInternal(uint64_t now) = 0;
|
||||||
virtual void addEntityInternal(EntityItemPointer entity) = 0;
|
virtual void addEntityInternal(EntityItemPointer entity) = 0;
|
||||||
virtual void removeEntityInternal(EntityItemPointer entity);
|
virtual void removeEntityInternal(EntityItemPointer entity);
|
||||||
virtual void changeEntityInternal(EntityItemPointer entity) = 0;
|
virtual void processChangedEntity(const EntityItemPointer& entity);
|
||||||
virtual void clearEntitiesInternal() = 0;
|
virtual void clearEntitiesInternal() = 0;
|
||||||
|
|
||||||
void expireMortalEntities(uint64_t now);
|
void expireMortalEntities(uint64_t now);
|
||||||
|
@ -114,11 +117,11 @@ private:
|
||||||
|
|
||||||
// We maintain multiple lists, each for its distinct purpose.
|
// We maintain multiple lists, each for its distinct purpose.
|
||||||
// An entity may be in more than one list.
|
// An entity may be in more than one list.
|
||||||
|
std::unordered_set<EntityItemPointer> _changedEntities; // all changes this frame
|
||||||
SetOfEntities _allEntities; // tracks all entities added the simulation
|
SetOfEntities _allEntities; // tracks all entities added the simulation
|
||||||
SetOfEntities _mortalEntities; // entities that have an expiry
|
SetOfEntities _mortalEntities; // entities that have an expiry
|
||||||
uint64_t _nextExpiry;
|
uint64_t _nextExpiry;
|
||||||
|
|
||||||
|
|
||||||
SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update()
|
SetOfEntities _entitiesToUpdate; // entities that need to call EntityItem::update()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2079,7 +2079,6 @@ void EntityTree::entityChanged(EntityItemPointer entity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTree::fixupNeedsParentFixups() {
|
void EntityTree::fixupNeedsParentFixups() {
|
||||||
PROFILE_RANGE(simulation_physics, "FixupParents");
|
|
||||||
MovingEntitiesOperator moveOperator;
|
MovingEntitiesOperator moveOperator;
|
||||||
QVector<EntityItemWeakPointer> entitiesToFixup;
|
QVector<EntityItemWeakPointer> entitiesToFixup;
|
||||||
{
|
{
|
||||||
|
@ -2189,11 +2188,19 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) {
|
||||||
_needsParentFixup.append(entity);
|
_needsParentFixup.append(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityTree::preUpdate() {
|
||||||
|
withWriteLock([&] {
|
||||||
|
fixupNeedsParentFixups();
|
||||||
|
if (_simulation) {
|
||||||
|
_simulation->processChangedEntities();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void EntityTree::update(bool simulate) {
|
void EntityTree::update(bool simulate) {
|
||||||
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
||||||
PerformanceTimer perfTimer("updateTree");
|
PerformanceTimer perfTimer("updateTree");
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
fixupNeedsParentFixups();
|
|
||||||
if (simulate && _simulation) {
|
if (simulate && _simulation) {
|
||||||
_simulation->updateEntities();
|
_simulation->updateEntities();
|
||||||
{
|
{
|
||||||
|
|
|
@ -109,9 +109,10 @@ public:
|
||||||
|
|
||||||
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const override;
|
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const override;
|
||||||
|
|
||||||
virtual void update() override { update(true); }
|
// Why preUpdate() and update()?
|
||||||
|
// Because sometimes we need to do stuff between the two.
|
||||||
void update(bool simulate);
|
void preUpdate() override;
|
||||||
|
void update(bool simulate = true) override;
|
||||||
|
|
||||||
// The newer API...
|
// The newer API...
|
||||||
void postAddEntity(EntityItemPointer entityItem);
|
void postAddEntity(EntityItemPointer entityItem);
|
||||||
|
|
|
@ -421,11 +421,6 @@ void ModelEntityItem::setAnimationFPS(float value) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
|
||||||
bool ModelEntityItem::shouldBePhysical() const {
|
|
||||||
return !isDead() && getShapeType() != SHAPE_TYPE_NONE && QUrl(_modelURL).isValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModelEntityItem::resizeJointArrays(int newSize) {
|
void ModelEntityItem::resizeJointArrays(int newSize) {
|
||||||
if (newSize < 0) {
|
if (newSize < 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -118,8 +118,6 @@ public:
|
||||||
const QString getTextures() const;
|
const QString getTextures() const;
|
||||||
void setTextures(const QString& textures);
|
void setTextures(const QString& textures);
|
||||||
|
|
||||||
virtual bool shouldBePhysical() const override;
|
|
||||||
|
|
||||||
virtual void setJointRotations(const QVector<glm::quat>& rotations);
|
virtual void setJointRotations(const QVector<glm::quat>& rotations);
|
||||||
virtual void setJointRotationsSet(const QVector<bool>& rotationsSet);
|
virtual void setJointRotationsSet(const QVector<bool>& rotationsSet);
|
||||||
virtual void setJointTranslations(const QVector<glm::vec3>& translations);
|
virtual void setJointTranslations(const QVector<glm::vec3>& translations);
|
||||||
|
|
|
@ -231,6 +231,8 @@ public:
|
||||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||||
bool& somethingChanged) override;
|
bool& somethingChanged) override;
|
||||||
|
|
||||||
|
bool shouldBePhysical() const override { return false; }
|
||||||
|
|
||||||
void setColor(const glm::u8vec3& value);
|
void setColor(const glm::u8vec3& value);
|
||||||
glm::u8vec3 getColor() const { return _particleProperties.color.gradient.target; }
|
glm::u8vec3 getColor() const { return _particleProperties.color.gradient.target; }
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,6 @@ class PolyVoxEntityItem : public EntityItem {
|
||||||
glm::vec3 getSurfacePositionAdjustment() const;
|
glm::vec3 getSurfacePositionAdjustment() const;
|
||||||
|
|
||||||
virtual ShapeType getShapeType() const override;
|
virtual ShapeType getShapeType() const override;
|
||||||
virtual bool shouldBePhysical() const override { return !isDead(); }
|
|
||||||
|
|
||||||
bool isEdged() const;
|
bool isEdged() const;
|
||||||
|
|
||||||
|
|
|
@ -84,8 +84,6 @@ public:
|
||||||
|
|
||||||
void setUnscaledDimensions(const glm::vec3& value) override;
|
void setUnscaledDimensions(const glm::vec3& value) override;
|
||||||
|
|
||||||
bool shouldBePhysical() const override { return !isDead(); }
|
|
||||||
|
|
||||||
bool supportsDetailedIntersection() const override;
|
bool supportsDetailedIntersection() const override;
|
||||||
bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
OctreeElementPointer& element, float& distance,
|
OctreeElementPointer& element, float& distance,
|
||||||
|
|
|
@ -85,7 +85,9 @@ void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||||
_entitiesThatNeedSimulationOwner.remove(entity);
|
_entitiesThatNeedSimulationOwner.remove(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
void SimpleEntitySimulation::processChangedEntity(const EntityItemPointer& entity) {
|
||||||
|
EntitySimulation::processChangedEntity(entity);
|
||||||
|
|
||||||
uint32_t flags = entity->getDirtyFlags();
|
uint32_t flags = entity->getDirtyFlags();
|
||||||
if ((flags & Simulation::DIRTY_SIMULATOR_ID) || (flags & Simulation::DIRTY_VELOCITIES)) {
|
if ((flags & Simulation::DIRTY_SIMULATOR_ID) || (flags & Simulation::DIRTY_VELOCITIES)) {
|
||||||
if (entity->getSimulatorID().isNull()) {
|
if (entity->getSimulatorID().isNull()) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ protected:
|
||||||
void updateEntitiesInternal(uint64_t now) override;
|
void updateEntitiesInternal(uint64_t now) override;
|
||||||
void addEntityInternal(EntityItemPointer entity) override;
|
void addEntityInternal(EntityItemPointer entity) override;
|
||||||
void removeEntityInternal(EntityItemPointer entity) override;
|
void removeEntityInternal(EntityItemPointer entity) override;
|
||||||
void changeEntityInternal(EntityItemPointer entity) override;
|
void processChangedEntity(const EntityItemPointer& entity) override;
|
||||||
void clearEntitiesInternal() override;
|
void clearEntitiesInternal() override;
|
||||||
|
|
||||||
void sortEntitiesThatMoved() override;
|
void sortEntitiesThatMoved() override;
|
||||||
|
|
|
@ -89,6 +89,8 @@
|
||||||
// (14) When an entity's ownership priority drops to YIELD (=1, below VOLUNTEER) other participants may
|
// (14) When an entity's ownership priority drops to YIELD (=1, below VOLUNTEER) other participants may
|
||||||
// bid for it immediately at VOLUNTEER.
|
// bid for it immediately at VOLUNTEER.
|
||||||
//
|
//
|
||||||
|
/* These declarations temporarily moved to SimulationFlags.h while we unravel some spaghetti dependencies.
|
||||||
|
* The intent is to move them back here once the dust settles.
|
||||||
const uint8_t YIELD_SIMULATION_PRIORITY = 1;
|
const uint8_t YIELD_SIMULATION_PRIORITY = 1;
|
||||||
const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1;
|
const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1;
|
||||||
const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
||||||
|
@ -101,6 +103,7 @@ const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY
|
||||||
// which really just means: things that collide with it will be bid at a priority level one lower
|
// which really just means: things that collide with it will be bid at a priority level one lower
|
||||||
const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||||
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY;
|
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY;
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
class SimulationOwner {
|
class SimulationOwner {
|
||||||
|
|
|
@ -62,6 +62,7 @@ public:
|
||||||
virtual bool isReadyToComputeShape() const override { return false; }
|
virtual bool isReadyToComputeShape() const override { return false; }
|
||||||
virtual void setShapeType(ShapeType type) override;
|
virtual void setShapeType(ShapeType type) override;
|
||||||
virtual ShapeType getShapeType() const override;
|
virtual ShapeType getShapeType() const override;
|
||||||
|
bool shouldBePhysical() const override { return false; }
|
||||||
|
|
||||||
QString getCompoundShapeURL() const;
|
QString getCompoundShapeURL() const;
|
||||||
virtual void setCompoundShapeURL(const QString& url);
|
virtual void setCompoundShapeURL(const QString& url);
|
||||||
|
|
|
@ -145,7 +145,10 @@ public:
|
||||||
virtual bool rootElementHasData() const { return false; }
|
virtual bool rootElementHasData() const { return false; }
|
||||||
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { }
|
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { }
|
||||||
|
|
||||||
virtual void update() { } // nothing to do by default
|
// Why preUpdate() and update()?
|
||||||
|
// Because EntityTree needs them.
|
||||||
|
virtual void preUpdate() { }
|
||||||
|
virtual void update(bool simulate = true) { }
|
||||||
|
|
||||||
OctreeElementPointer getRoot() { return _rootElement; }
|
OctreeElementPointer getRoot() { return _rootElement; }
|
||||||
|
|
||||||
|
|
|
@ -242,6 +242,7 @@ bool OctreePersistThread::backupCurrentFile() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreePersistThread::process() {
|
void OctreePersistThread::process() {
|
||||||
|
_tree->preUpdate();
|
||||||
_tree->update();
|
_tree->update();
|
||||||
|
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
|
|
@ -25,23 +25,6 @@
|
||||||
#include "PhysicsHelpers.h"
|
#include "PhysicsHelpers.h"
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
|
||||||
#include "EntityTree.h"
|
|
||||||
|
|
||||||
bool EntityMotionState::entityTreeIsLocked() const {
|
|
||||||
EntityTreeElementPointer element = _entity->getElement();
|
|
||||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
|
||||||
if (!tree) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
bool entityTreeIsLocked() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50;
|
const uint8_t LOOPS_FOR_SIMULATION_ORPHAN = 50;
|
||||||
const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5;
|
const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5;
|
||||||
|
|
||||||
|
@ -74,7 +57,6 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
||||||
|
|
||||||
_type = MOTIONSTATE_TYPE_ENTITY;
|
_type = MOTIONSTATE_TYPE_ENTITY;
|
||||||
assert(_entity);
|
assert(_entity);
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
setMass(_entity->computeMass());
|
setMass(_entity->computeMass());
|
||||||
// we need the side-effects of EntityMotionState::setShape() so we call it explicitly here
|
// we need the side-effects of EntityMotionState::setShape() so we call it explicitly here
|
||||||
// rather than pass the legit shape pointer to the ObjectMotionState ctor above.
|
// rather than pass the legit shape pointer to the ObjectMotionState ctor above.
|
||||||
|
@ -143,7 +125,6 @@ void EntityMotionState::handleDeactivation() {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
updateServerPhysicsVariables();
|
updateServerPhysicsVariables();
|
||||||
ObjectMotionState::handleEasyChanges(flags);
|
ObjectMotionState::handleEasyChanges(flags);
|
||||||
|
|
||||||
|
@ -191,17 +172,10 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// virtual
|
|
||||||
bool EntityMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
|
||||||
updateServerPhysicsVariables();
|
|
||||||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
|
PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
|
||||||
if (!_entity) {
|
if (!_entity) {
|
||||||
return MOTION_TYPE_STATIC;
|
return MOTION_TYPE_STATIC;
|
||||||
}
|
}
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
|
|
||||||
if (_entity->getLocked()) {
|
if (_entity->getLocked()) {
|
||||||
if (_entity->isMoving()) {
|
if (_entity->isMoving()) {
|
||||||
|
@ -226,7 +200,6 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::isMoving() const {
|
bool EntityMotionState::isMoving() const {
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
return _entity && _entity->isMovingRelativeToParent();
|
return _entity && _entity->isMovingRelativeToParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +213,6 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||||
if (!_entity) {
|
if (!_entity) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
if (_motionType == MOTION_TYPE_KINEMATIC) {
|
if (_motionType == MOTION_TYPE_KINEMATIC) {
|
||||||
BT_PROFILE("kinematicIntegration");
|
BT_PROFILE("kinematicIntegration");
|
||||||
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
||||||
|
@ -271,7 +243,6 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||||
// This callback is invoked by the physics simulation at the end of each simulation step...
|
// This callback is invoked by the physics simulation at the end of each simulation step...
|
||||||
// iff the corresponding RigidBody is DYNAMIC and ACTIVE.
|
// iff the corresponding RigidBody is DYNAMIC and ACTIVE.
|
||||||
void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
measureBodyAcceleration();
|
measureBodyAcceleration();
|
||||||
|
|
||||||
// If transform or velocities are flagged as dirty it means a network or scripted change
|
// If transform or velocities are flagged as dirty it means a network or scripted change
|
||||||
|
@ -309,19 +280,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// virtual and protected
|
|
||||||
bool EntityMotionState::isReadyToComputeShape() const {
|
|
||||||
return _entity->isReadyToComputeShape();
|
|
||||||
}
|
|
||||||
|
|
||||||
// virtual and protected
|
|
||||||
const btCollisionShape* EntityMotionState::computeNewShape() {
|
|
||||||
ShapeInfo shapeInfo;
|
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
_entity->computeShapeInfo(shapeInfo);
|
|
||||||
return getShapeManager()->getShape(shapeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
||||||
|
|
||||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
@ -439,7 +397,6 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend");
|
DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend");
|
||||||
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
||||||
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
|
|
||||||
// this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
|
// this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
|
||||||
assert(!(_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
|
assert(!(_entity->isAvatarEntity() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
|
||||||
|
@ -505,7 +462,6 @@ void EntityMotionState::updateSendVelocities() {
|
||||||
|
|
||||||
void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t step) {
|
void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "Bid");
|
DETAILED_PROFILE_RANGE(simulation_physics, "Bid");
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
|
|
||||||
updateSendVelocities();
|
updateSendVelocities();
|
||||||
|
|
||||||
|
@ -546,7 +502,6 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s
|
||||||
|
|
||||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "Send");
|
DETAILED_PROFILE_RANGE(simulation_physics, "Send");
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
assert(isLocallyOwned());
|
assert(isLocallyOwned());
|
||||||
|
|
||||||
updateSendVelocities();
|
updateSendVelocities();
|
||||||
|
@ -645,8 +600,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
_bumpedPriority = 0;
|
_bumpedPriority = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EntityMotionState::getIncomingDirtyFlags() {
|
uint32_t EntityMotionState::getIncomingDirtyFlags() const {
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
uint32_t dirtyFlags = 0;
|
uint32_t dirtyFlags = 0;
|
||||||
if (_body && _entity) {
|
if (_body && _entity) {
|
||||||
dirtyFlags = _entity->getDirtyFlags();
|
dirtyFlags = _entity->getDirtyFlags();
|
||||||
|
@ -676,10 +630,9 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() {
|
||||||
return dirtyFlags;
|
return dirtyFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::clearIncomingDirtyFlags() {
|
void EntityMotionState::clearIncomingDirtyFlags(uint32_t mask) {
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
if (_body && _entity) {
|
if (_body && _entity) {
|
||||||
_entity->clearDirtyFlags(DIRTY_PHYSICS_FLAGS);
|
_entity->clearDirtyFlags(mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +647,6 @@ void EntityMotionState::slaveBidPriority() {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
QUuid EntityMotionState::getSimulatorID() const {
|
QUuid EntityMotionState::getSimulatorID() const {
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
return _entity->getSimulatorID();
|
return _entity->getSimulatorID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,6 +714,10 @@ glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const {
|
||||||
return _measuredAcceleration * _measuredDeltaTime;
|
return _measuredAcceleration * _measuredDeltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntityMotionState::shouldBeInPhysicsSimulation() const {
|
||||||
|
return _region < workload::Region::R3 && _entity->shouldBePhysical();
|
||||||
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void EntityMotionState::setMotionType(PhysicsMotionType motionType) {
|
void EntityMotionState::setMotionType(PhysicsMotionType motionType) {
|
||||||
ObjectMotionState::setMotionType(motionType);
|
ObjectMotionState::setMotionType(motionType);
|
||||||
|
@ -770,7 +726,6 @@ void EntityMotionState::setMotionType(PhysicsMotionType motionType) {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
QString EntityMotionState::getName() const {
|
QString EntityMotionState::getName() const {
|
||||||
assert(entityTreeIsLocked());
|
|
||||||
return _entity->getName();
|
return _entity->getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -788,6 +743,15 @@ bool EntityMotionState::shouldSendBid() const {
|
||||||
&& !_entity->getLocked();
|
&& !_entity->getLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityMotionState::setRigidBody(btRigidBody* body) {
|
||||||
|
ObjectMotionState::setRigidBody(body);
|
||||||
|
if (_body) {
|
||||||
|
_entity->markSpecialFlags(Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION);
|
||||||
|
} else {
|
||||||
|
_entity->clearSpecialFlags(Simulation::SPECIAL_FLAG_IN_PHYSICS_SIMULATION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t EntityMotionState::computeFinalBidPriority() const {
|
uint8_t EntityMotionState::computeFinalBidPriority() const {
|
||||||
return (_region == workload::Region::R1) ?
|
return (_region == workload::Region::R1) ?
|
||||||
glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) : 0;
|
glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) : 0;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#ifndef hifi_EntityMotionState_h
|
#ifndef hifi_EntityMotionState_h
|
||||||
#define hifi_EntityMotionState_h
|
#define hifi_EntityMotionState_h
|
||||||
|
|
||||||
|
#include <EntityItem.h>
|
||||||
#include <EntityTypes.h>
|
#include <EntityTypes.h>
|
||||||
#include <AACube.h>
|
#include <AACube.h>
|
||||||
#include <workload/Region.h>
|
#include <workload/Region.h>
|
||||||
|
@ -38,7 +39,6 @@ public:
|
||||||
|
|
||||||
void handleDeactivation();
|
void handleDeactivation();
|
||||||
virtual void handleEasyChanges(uint32_t& flags) override;
|
virtual void handleEasyChanges(uint32_t& flags) override;
|
||||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override;
|
|
||||||
|
|
||||||
/// \return PhysicsMotionType based on params set in EntityItem
|
/// \return PhysicsMotionType based on params set in EntityItem
|
||||||
virtual PhysicsMotionType computePhysicsMotionType() const override;
|
virtual PhysicsMotionType computePhysicsMotionType() const override;
|
||||||
|
@ -55,8 +55,8 @@ public:
|
||||||
void sendBid(OctreeEditPacketSender* packetSender, uint32_t step);
|
void sendBid(OctreeEditPacketSender* packetSender, uint32_t step);
|
||||||
void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step);
|
void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step);
|
||||||
|
|
||||||
virtual uint32_t getIncomingDirtyFlags() override;
|
virtual uint32_t getIncomingDirtyFlags() const override;
|
||||||
virtual void clearIncomingDirtyFlags() override;
|
virtual void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) override;
|
||||||
|
|
||||||
virtual float getObjectRestitution() const override { return _entity->getRestitution(); }
|
virtual float getObjectRestitution() const override { return _entity->getRestitution(); }
|
||||||
virtual float getObjectFriction() const override { return _entity->getFriction(); }
|
virtual float getObjectFriction() const override { return _entity->getFriction(); }
|
||||||
|
@ -84,6 +84,7 @@ public:
|
||||||
void measureBodyAcceleration();
|
void measureBodyAcceleration();
|
||||||
|
|
||||||
virtual QString getName() const override;
|
virtual QString getName() const override;
|
||||||
|
ShapeType getShapeType() const override { return _entity->getShapeType(); }
|
||||||
|
|
||||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||||
|
|
||||||
|
@ -99,6 +100,8 @@ public:
|
||||||
void saveKinematicState(btScalar timeStep) override;
|
void saveKinematicState(btScalar timeStep) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void setRigidBody(btRigidBody* body) override;
|
||||||
|
|
||||||
uint8_t computeFinalBidPriority() const;
|
uint8_t computeFinalBidPriority() const;
|
||||||
void updateSendVelocities();
|
void updateSendVelocities();
|
||||||
uint64_t getNextBidExpiry() const { return _nextBidExpiry; }
|
uint64_t getNextBidExpiry() const { return _nextBidExpiry; }
|
||||||
|
@ -112,12 +115,8 @@ protected:
|
||||||
|
|
||||||
void clearObjectVelocities() const;
|
void clearObjectVelocities() const;
|
||||||
|
|
||||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
bool isInPhysicsSimulation() const { return _body != nullptr; }
|
||||||
bool entityTreeIsLocked() const;
|
bool shouldBeInPhysicsSimulation() const;
|
||||||
#endif
|
|
||||||
|
|
||||||
bool isReadyToComputeShape() const override;
|
|
||||||
const btCollisionShape* computeNewShape() override;
|
|
||||||
void setMotionType(PhysicsMotionType motionType) override;
|
void setMotionType(PhysicsMotionType motionType) override;
|
||||||
|
|
||||||
// EntityMotionState keeps a SharedPointer to its EntityItem which is only set in the CTOR
|
// EntityMotionState keeps a SharedPointer to its EntityItem which is only set in the CTOR
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
|
|
||||||
#include "ObjectActionTractor.h"
|
#include "ObjectActionTractor.h"
|
||||||
|
|
||||||
#include "QVariantGLM.h"
|
#include <EntityItem.h>
|
||||||
|
#include <QVariantGLM.h>
|
||||||
|
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
#include "QVariantGLM.h"
|
#include <EntityItem.h>
|
||||||
|
#include <QVariantGLM.h>
|
||||||
|
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
|
|
||||||
const uint16_t ObjectActionTravelOriented::actionVersion = 1;
|
const uint16_t ObjectActionTravelOriented::actionVersion = 1;
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
|
|
||||||
#include <btBulletDynamicsCommon.h>
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
|
||||||
|
#include <EntityItemID.h>
|
||||||
#include <shared/ReadWriteLockable.h>
|
#include <shared/ReadWriteLockable.h>
|
||||||
|
#include <SpatiallyNestable.h>
|
||||||
|
|
||||||
#include "ObjectMotionState.h"
|
#include "ObjectMotionState.h"
|
||||||
#include "BulletUtil.h"
|
#include "BulletUtil.h"
|
||||||
|
|
|
@ -198,9 +198,14 @@ void ObjectMotionState::setShape(const btCollisionShape* shape) {
|
||||||
getShapeManager()->releaseShape(_shape);
|
getShapeManager()->releaseShape(_shape);
|
||||||
}
|
}
|
||||||
_shape = shape;
|
_shape = shape;
|
||||||
if (_body && _type != MOTIONSTATE_TYPE_DETAILED) {
|
if (_body) {
|
||||||
|
assert(_shape);
|
||||||
|
_body->setCollisionShape(const_cast<btCollisionShape*>(_shape));
|
||||||
updateCCDConfiguration();
|
updateCCDConfiguration();
|
||||||
}
|
}
|
||||||
|
} else if (shape) {
|
||||||
|
// we need to release unused reference to shape
|
||||||
|
getShapeManager()->releaseShape(shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,50 +290,6 @@ void ObjectMotionState::handleEasyChanges(uint32_t& flags) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
|
||||||
assert(_body && _shape);
|
|
||||||
if (flags & Simulation::DIRTY_SHAPE) {
|
|
||||||
// make sure the new shape is valid
|
|
||||||
if (!isReadyToComputeShape()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const btCollisionShape* newShape = computeNewShape();
|
|
||||||
if (!newShape) {
|
|
||||||
qCDebug(physics) << "Warning: failed to generate new shape!";
|
|
||||||
// failed to generate new shape! --> keep old shape and remove shape-change flag
|
|
||||||
flags &= ~Simulation::DIRTY_SHAPE;
|
|
||||||
// TODO: force this object out of PhysicsEngine rather than just use the old shape
|
|
||||||
if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) {
|
|
||||||
// no HARD flags remain, so do any EASY changes
|
|
||||||
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
|
||||||
handleEasyChanges(flags);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_shape == newShape) {
|
|
||||||
// the shape didn't actually change, so we clear the DIRTY_SHAPE flag
|
|
||||||
flags &= ~Simulation::DIRTY_SHAPE;
|
|
||||||
// and clear the reference we just created
|
|
||||||
getShapeManager()->releaseShape(_shape);
|
|
||||||
} else {
|
|
||||||
_body->setCollisionShape(const_cast<btCollisionShape*>(newShape));
|
|
||||||
setShape(newShape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
|
||||||
handleEasyChanges(flags);
|
|
||||||
}
|
|
||||||
// it is possible there are no HARD flags at this point (if DIRTY_SHAPE was removed)
|
|
||||||
// so we check again before we reinsert:
|
|
||||||
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
|
|
||||||
engine->reinsertObject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectMotionState::updateBodyMaterialProperties() {
|
void ObjectMotionState::updateBodyMaterialProperties() {
|
||||||
_body->setRestitution(getObjectRestitution());
|
_body->setRestitution(getObjectRestitution());
|
||||||
_body->setFriction(getObjectFriction());
|
_body->setFriction(getObjectFriction());
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
#include <EntityItem.h>
|
#include <SimulationFlags.h>
|
||||||
|
|
||||||
#include "ContactInfo.h"
|
#include "ContactInfo.h"
|
||||||
#include "ShapeManager.h"
|
#include "ShapeManager.h"
|
||||||
|
@ -100,7 +100,6 @@ public:
|
||||||
virtual ~ObjectMotionState();
|
virtual ~ObjectMotionState();
|
||||||
|
|
||||||
virtual void handleEasyChanges(uint32_t& flags);
|
virtual void handleEasyChanges(uint32_t& flags);
|
||||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine);
|
|
||||||
|
|
||||||
void updateBodyMaterialProperties();
|
void updateBodyMaterialProperties();
|
||||||
void updateBodyVelocities();
|
void updateBodyVelocities();
|
||||||
|
@ -123,11 +122,12 @@ public:
|
||||||
glm::vec3 getBodyAngularVelocity() const;
|
glm::vec3 getBodyAngularVelocity() const;
|
||||||
virtual glm::vec3 getObjectLinearVelocityChange() const;
|
virtual glm::vec3 getObjectLinearVelocityChange() const;
|
||||||
|
|
||||||
virtual uint32_t getIncomingDirtyFlags() = 0;
|
virtual uint32_t getIncomingDirtyFlags() const = 0;
|
||||||
virtual void clearIncomingDirtyFlags() = 0;
|
virtual void clearIncomingDirtyFlags(uint32_t mask = DIRTY_PHYSICS_FLAGS) = 0;
|
||||||
|
|
||||||
virtual PhysicsMotionType computePhysicsMotionType() const = 0;
|
virtual PhysicsMotionType computePhysicsMotionType() const = 0;
|
||||||
|
|
||||||
|
virtual bool needsNewShape() const { return _shape == nullptr || getIncomingDirtyFlags() & Simulation::DIRTY_SHAPE; }
|
||||||
const btCollisionShape* getShape() const { return _shape; }
|
const btCollisionShape* getShape() const { return _shape; }
|
||||||
btRigidBody* getRigidBody() const { return _body; }
|
btRigidBody* getRigidBody() const { return _body; }
|
||||||
|
|
||||||
|
@ -154,6 +154,7 @@ public:
|
||||||
virtual void bump(uint8_t priority) {}
|
virtual void bump(uint8_t priority) {}
|
||||||
|
|
||||||
virtual QString getName() const { return ""; }
|
virtual QString getName() const { return ""; }
|
||||||
|
virtual ShapeType getShapeType() const = 0;
|
||||||
|
|
||||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const = 0;
|
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const = 0;
|
||||||
|
|
||||||
|
@ -172,8 +173,6 @@ public:
|
||||||
friend class PhysicsEngine;
|
friend class PhysicsEngine;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool isReadyToComputeShape() const = 0;
|
|
||||||
virtual const btCollisionShape* computeNewShape() = 0;
|
|
||||||
virtual void setMotionType(PhysicsMotionType motionType);
|
virtual void setMotionType(PhysicsMotionType motionType);
|
||||||
void updateCCDConfiguration();
|
void updateCCDConfiguration();
|
||||||
|
|
||||||
|
@ -187,7 +186,7 @@ protected:
|
||||||
btRigidBody* _body { nullptr };
|
btRigidBody* _body { nullptr };
|
||||||
float _density { 1.0f };
|
float _density { 1.0f };
|
||||||
|
|
||||||
// ACTION_CAN_CONTROL_KINEMATIC_OBJECT_HACK: These date members allow an Action
|
// ACTION_CAN_CONTROL_KINEMATIC_OBJECT_HACK: These data members allow an Action
|
||||||
// to operate on a kinematic object without screwing up our default kinematic integration
|
// to operate on a kinematic object without screwing up our default kinematic integration
|
||||||
// which is done in the MotionState::getWorldTransform().
|
// which is done in the MotionState::getWorldTransform().
|
||||||
mutable uint32_t _lastKinematicStep;
|
mutable uint32_t _lastKinematicStep;
|
||||||
|
|
|
@ -131,10 +131,10 @@ void PhysicalEntitySimulation::takeDeadAvatarEntities(SetOfEntities& deadEntitie
|
||||||
_deadAvatarEntities.clear();
|
_deadAvatarEntities.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
void PhysicalEntitySimulation::processChangedEntity(const EntityItemPointer& entity) {
|
||||||
|
EntitySimulation::processChangedEntity(entity);
|
||||||
|
|
||||||
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
|
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
|
||||||
QMutexLocker lock(&_mutex);
|
|
||||||
assert(entity);
|
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
uint8_t region = _space->getRegion(entity->getSpaceIndex());
|
uint8_t region = _space->getRegion(entity->getSpaceIndex());
|
||||||
bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();
|
bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();
|
||||||
|
@ -156,7 +156,6 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||||
|
|
||||||
// remove from the physical simulation
|
// remove from the physical simulation
|
||||||
_incomingChanges.remove(motionState);
|
_incomingChanges.remove(motionState);
|
||||||
_physicalObjects.remove(motionState);
|
|
||||||
removeOwnershipData(motionState);
|
removeOwnershipData(motionState);
|
||||||
_entitiesToRemoveFromPhysics.insert(entity);
|
_entitiesToRemoveFromPhysics.insert(entity);
|
||||||
if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||||
|
@ -227,44 +226,68 @@ void PhysicalEntitySimulation::prepareEntityForDelete(EntityItemPointer entity)
|
||||||
}
|
}
|
||||||
// end EntitySimulation overrides
|
// end EntitySimulation overrides
|
||||||
|
|
||||||
const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhysics() {
|
void PhysicalEntitySimulation::buildMotionStatesForEntitiesThatNeedThem() {
|
||||||
QMutexLocker lock(&_mutex);
|
// this lambda for when we decide to actually build the motionState
|
||||||
for (auto entity: _entitiesToRemoveFromPhysics) {
|
auto buildMotionState = [&](btCollisionShape* shape, EntityItemPointer entity) {
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = new EntityMotionState(shape, entity);
|
||||||
assert(motionState);
|
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
||||||
// TODO CLEan this, just a n extra check to avoid the crash that shouldn;t happen
|
motionState->setRegion(_space->getRegion(entity->getSpaceIndex()));
|
||||||
if (motionState) {
|
_physicalObjects.insert(motionState);
|
||||||
_entitiesToAddToPhysics.remove(entity);
|
_incomingChanges.insert(motionState);
|
||||||
if (entity->isDead() && entity->getElement()) {
|
};
|
||||||
_deadEntities.insert(entity);
|
|
||||||
|
uint32_t deliveryCount = ObjectMotionState::getShapeManager()->getWorkDeliveryCount();
|
||||||
|
if (deliveryCount != _lastWorkDeliveryCount) {
|
||||||
|
// new off-thread shapes have arrived --> find adds whose shapes have arrived
|
||||||
|
_lastWorkDeliveryCount = deliveryCount;
|
||||||
|
ShapeRequests::iterator requestItr = _shapeRequests.begin();
|
||||||
|
while (requestItr != _shapeRequests.end()) {
|
||||||
|
EntityItemPointer entity = requestItr->entity;
|
||||||
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
|
if (!motionState) {
|
||||||
|
// this is an ADD because motionState doesn't exist yet
|
||||||
|
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShapeByKey(requestItr->shapeHash));
|
||||||
|
if (shape) {
|
||||||
|
// shape is ready at last!
|
||||||
|
// But the entity's desired shape might have changed since last requested
|
||||||
|
// --> rebuild the ShapeInfo to verify hash
|
||||||
|
// TODO? is there a better way to do this?
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
|
entity->computeShapeInfo(shapeInfo);
|
||||||
|
if (shapeInfo.getHash() != requestItr->shapeHash) {
|
||||||
|
// bummer, the hashes are different and we no longer want the shape we've received
|
||||||
|
ObjectMotionState::getShapeManager()->releaseShape(shape);
|
||||||
|
// try again
|
||||||
|
shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||||
|
if (shape) {
|
||||||
|
buildMotionState(shape, entity);
|
||||||
|
requestItr = _shapeRequests.erase(requestItr);
|
||||||
|
} else {
|
||||||
|
requestItr->shapeHash = shapeInfo.getHash();
|
||||||
|
++requestItr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildMotionState(shape, entity);
|
||||||
|
requestItr = _shapeRequests.erase(requestItr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// shape not ready
|
||||||
|
++requestItr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this is a CHANGE because motionState already exists
|
||||||
|
if (ObjectMotionState::getShapeManager()->hasShapeWithKey(requestItr->shapeHash)) {
|
||||||
|
entity->markDirtyFlags(Simulation::DIRTY_SHAPE);
|
||||||
|
_incomingChanges.insert(motionState);
|
||||||
|
requestItr = _shapeRequests.erase(requestItr);
|
||||||
|
} else {
|
||||||
|
// shape not ready
|
||||||
|
++requestItr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_incomingChanges.remove(motionState);
|
|
||||||
removeOwnershipData(motionState);
|
|
||||||
_physicalObjects.remove(motionState);
|
|
||||||
|
|
||||||
// remember this motionState and delete it later (after removing its RigidBody from the PhysicsEngine)
|
|
||||||
_objectsToDelete.push_back(motionState);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_entitiesToRemoveFromPhysics.clear();
|
|
||||||
return _objectsToDelete;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhysicalEntitySimulation::deleteObjectsRemovedFromPhysics() {
|
|
||||||
QMutexLocker lock(&_mutex);
|
|
||||||
for (auto motionState : _objectsToDelete) {
|
|
||||||
// someday when we invert the entities/physics lib dependencies we can let EntityItem delete its own PhysicsInfo
|
|
||||||
// until then we must do it here
|
|
||||||
// NOTE: a reference to the EntityItemPointer is released in the EntityMotionState::dtor
|
|
||||||
delete motionState;
|
|
||||||
}
|
|
||||||
_objectsToDelete.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
|
||||||
result.clear();
|
|
||||||
QMutexLocker lock(&_mutex);
|
|
||||||
SetOfEntities::iterator entityItr = _entitiesToAddToPhysics.begin();
|
SetOfEntities::iterator entityItr = _entitiesToAddToPhysics.begin();
|
||||||
while (entityItr != _entitiesToAddToPhysics.end()) {
|
while (entityItr != _entitiesToAddToPhysics.end()) {
|
||||||
EntityItemPointer entity = (*entityItr);
|
EntityItemPointer entity = (*entityItr);
|
||||||
|
@ -273,7 +296,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
|
||||||
prepareEntityForDelete(entity);
|
prepareEntityForDelete(entity);
|
||||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||||
} else if (!entity->shouldBePhysical()) {
|
} else if (!entity->shouldBePhysical()) {
|
||||||
// this entity should no longer be on the internal _entitiesToAddToPhysics
|
// this entity should no longer be on _entitiesToAddToPhysics
|
||||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||||
if (entity->isMovingRelativeToParent()) {
|
if (entity->isMovingRelativeToParent()) {
|
||||||
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||||
|
@ -282,53 +305,149 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (entity->isReadyToComputeShape()) {
|
} else if (entity->isReadyToComputeShape()) {
|
||||||
ShapeInfo shapeInfo;
|
ShapeRequest shapeRequest(entity);
|
||||||
entity->computeShapeInfo(shapeInfo);
|
ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest);
|
||||||
int numPoints = shapeInfo.getLargestSubshapePointCount();
|
if (requestItr == _shapeRequests.end()) {
|
||||||
if (shapeInfo.getType() == SHAPE_TYPE_COMPOUND) {
|
// not waiting for a shape (yet)
|
||||||
if (numPoints > MAX_HULL_POINTS) {
|
ShapeInfo shapeInfo;
|
||||||
qWarning() << "convex hull with" << numPoints
|
entity->computeShapeInfo(shapeInfo);
|
||||||
<< "points for entity" << entity->getName()
|
uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount();
|
||||||
<< "at" << entity->getWorldPosition() << " will be reduced";
|
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||||
|
if (shape) {
|
||||||
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
|
if (!motionState) {
|
||||||
|
buildMotionState(shape, entity);
|
||||||
|
} else {
|
||||||
|
// Is it possible to fall in here?
|
||||||
|
// entity shouldn't be on _entitiesToAddToPhysics list if it already has a motionState.
|
||||||
|
// but just in case...
|
||||||
|
motionState->setShape(shape);
|
||||||
|
motionState->setRegion(_space->getRegion(entity->getSpaceIndex()));
|
||||||
|
_physicalObjects.insert(motionState);
|
||||||
|
_incomingChanges.insert(motionState);
|
||||||
|
}
|
||||||
|
} else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) {
|
||||||
|
// shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait
|
||||||
|
shapeRequest.shapeHash = shapeInfo.getHash();
|
||||||
|
_shapeRequests.insert(shapeRequest);
|
||||||
|
} else {
|
||||||
|
// failed to build shape --> will not be added
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||||
if (shape) {
|
|
||||||
EntityMotionState* motionState = new EntityMotionState(shape, entity);
|
|
||||||
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
|
||||||
_physicalObjects.insert(motionState);
|
|
||||||
result.push_back(motionState);
|
|
||||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
|
||||||
|
|
||||||
// make sure the motionState's region is up-to-date before it is actually added to physics
|
|
||||||
motionState->setRegion(_space->getRegion(entity->getSpaceIndex()));
|
|
||||||
} else {
|
|
||||||
//qWarning() << "Failed to generate new shape for entity." << entity->getName();
|
|
||||||
++entityItr;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
++entityItr;
|
++entityItr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::setObjectsToChange(const VectorOfMotionStates& objectsToChange) {
|
void PhysicalEntitySimulation::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
for (auto object : objectsToChange) {
|
// entities being removed
|
||||||
_incomingChanges.insert(static_cast<EntityMotionState*>(object));
|
for (auto entity : _entitiesToRemoveFromPhysics) {
|
||||||
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
|
if (motionState) {
|
||||||
|
transaction.objectsToRemove.push_back(motionState);
|
||||||
|
_incomingChanges.remove(motionState);
|
||||||
|
}
|
||||||
|
if (_shapeRequests.size() > 0) {
|
||||||
|
ShapeRequest shapeRequest(entity);
|
||||||
|
ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest);
|
||||||
|
if (requestItr != _shapeRequests.end()) {
|
||||||
|
_shapeRequests.erase(requestItr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
_entitiesToRemoveFromPhysics.clear();
|
||||||
|
|
||||||
void PhysicalEntitySimulation::getObjectsToChange(VectorOfMotionStates& result) {
|
// entities to add
|
||||||
result.clear();
|
buildMotionStatesForEntitiesThatNeedThem();
|
||||||
QMutexLocker lock(&_mutex);
|
|
||||||
for (auto stateItr : _incomingChanges) {
|
// motionStates with changed entities: delete, add, or change
|
||||||
EntityMotionState* motionState = &(*stateItr);
|
for (auto& object : _incomingChanges) {
|
||||||
result.push_back(motionState);
|
uint32_t unhandledFlags = object->getIncomingDirtyFlags();
|
||||||
|
|
||||||
|
uint32_t handledFlags = EASY_DIRTY_PHYSICS_FLAGS;
|
||||||
|
bool isInPhysicsSimulation = object->isInPhysicsSimulation();
|
||||||
|
bool shouldBeInPhysicsSimulation = object->shouldBeInPhysicsSimulation();
|
||||||
|
if (!shouldBeInPhysicsSimulation && isInPhysicsSimulation) {
|
||||||
|
transaction.objectsToRemove.push_back(object);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool needsNewShape = object->needsNewShape();
|
||||||
|
if (needsNewShape) {
|
||||||
|
ShapeType shapeType = object->getShapeType();
|
||||||
|
if (shapeType == SHAPE_TYPE_STATIC_MESH) {
|
||||||
|
ShapeRequest shapeRequest(object->_entity);
|
||||||
|
ShapeRequests::iterator requestItr = _shapeRequests.find(shapeRequest);
|
||||||
|
if (requestItr == _shapeRequests.end()) {
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
|
object->_entity->computeShapeInfo(shapeInfo);
|
||||||
|
uint32_t requestCount = ObjectMotionState::getShapeManager()->getWorkRequestCount();
|
||||||
|
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||||
|
if (shape) {
|
||||||
|
object->setShape(shape);
|
||||||
|
handledFlags |= Simulation::DIRTY_SHAPE;
|
||||||
|
needsNewShape = false;
|
||||||
|
} else if (requestCount != ObjectMotionState::getShapeManager()->getWorkRequestCount()) {
|
||||||
|
// shape doesn't exist but a new worker has been spawned to build it --> add to shapeRequests and wait
|
||||||
|
shapeRequest.shapeHash = shapeInfo.getHash();
|
||||||
|
_shapeRequests.insert(shapeRequest);
|
||||||
|
} else {
|
||||||
|
// failed to build shape --> will not be added/updated
|
||||||
|
handledFlags |= Simulation::DIRTY_SHAPE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// continue waiting for shape request
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
|
object->_entity->computeShapeInfo(shapeInfo);
|
||||||
|
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||||
|
if (shape) {
|
||||||
|
object->setShape(shape);
|
||||||
|
handledFlags |= Simulation::DIRTY_SHAPE;
|
||||||
|
needsNewShape = false;
|
||||||
|
} else {
|
||||||
|
// failed to build shape --> will not be added
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isInPhysicsSimulation) {
|
||||||
|
if (needsNewShape) {
|
||||||
|
// skip it
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
transaction.objectsToAdd.push_back(object);
|
||||||
|
handledFlags = DIRTY_PHYSICS_FLAGS;
|
||||||
|
unhandledFlags = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unhandledFlags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||||
|
object->handleEasyChanges(unhandledFlags);
|
||||||
|
}
|
||||||
|
if (unhandledFlags & (Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_COLLISION_GROUP | (handledFlags & Simulation::DIRTY_SHAPE))) {
|
||||||
|
transaction.objectsToReinsert.push_back(object);
|
||||||
|
handledFlags |= HARD_DIRTY_PHYSICS_FLAGS;
|
||||||
|
} else if (unhandledFlags & Simulation::DIRTY_PHYSICS_ACTIVATION && object->getRigidBody()->isStaticObject()) {
|
||||||
|
transaction.activeStaticObjects.push_back(object);
|
||||||
|
}
|
||||||
|
object->clearIncomingDirtyFlags(handledFlags);
|
||||||
}
|
}
|
||||||
_incomingChanges.clear();
|
_incomingChanges.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicalEntitySimulation::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||||
|
// things on objectsToRemove are ready for delete
|
||||||
|
for (auto object : transaction.objectsToRemove) {
|
||||||
|
_physicalObjects.remove(object);
|
||||||
|
delete object;
|
||||||
|
}
|
||||||
|
transaction.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates) {
|
void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates) {
|
||||||
bool serverlessMode = getEntityTree()->isServerlessMode();
|
bool serverlessMode = getEntityTree()->isServerlessMode();
|
||||||
for (auto stateItr : motionStates) {
|
for (auto stateItr : motionStates) {
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#define hifi_PhysicalEntitySimulation_h
|
#define hifi_PhysicalEntitySimulation_h
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <btBulletDynamicsCommon.h>
|
#include <btBulletDynamicsCommon.h>
|
||||||
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
|
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
|
||||||
|
@ -70,7 +72,7 @@ protected: // only called by EntitySimulation
|
||||||
virtual void updateEntitiesInternal(uint64_t now) override;
|
virtual void updateEntitiesInternal(uint64_t now) override;
|
||||||
virtual void addEntityInternal(EntityItemPointer entity) override;
|
virtual void addEntityInternal(EntityItemPointer entity) override;
|
||||||
virtual void removeEntityInternal(EntityItemPointer entity) override;
|
virtual void removeEntityInternal(EntityItemPointer entity) override;
|
||||||
virtual void changeEntityInternal(EntityItemPointer entity) override;
|
void processChangedEntity(const EntityItemPointer& entity) override;
|
||||||
virtual void clearEntitiesInternal() override;
|
virtual void clearEntitiesInternal() override;
|
||||||
|
|
||||||
void removeOwnershipData(EntityMotionState* motionState);
|
void removeOwnershipData(EntityMotionState* motionState);
|
||||||
|
@ -79,12 +81,8 @@ protected: // only called by EntitySimulation
|
||||||
public:
|
public:
|
||||||
virtual void prepareEntityForDelete(EntityItemPointer entity) override;
|
virtual void prepareEntityForDelete(EntityItemPointer entity) override;
|
||||||
|
|
||||||
const VectorOfMotionStates& getObjectsToRemoveFromPhysics();
|
void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||||
void deleteObjectsRemovedFromPhysics();
|
void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||||
|
|
||||||
void getObjectsToAddToPhysics(VectorOfMotionStates& result);
|
|
||||||
void setObjectsToChange(const VectorOfMotionStates& objectsToChange);
|
|
||||||
void getObjectsToChange(VectorOfMotionStates& result);
|
|
||||||
|
|
||||||
void handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates);
|
void handleDeactivatedMotionStates(const VectorOfMotionStates& motionStates);
|
||||||
void handleChangedMotionStates(const VectorOfMotionStates& motionStates);
|
void handleChangedMotionStates(const VectorOfMotionStates& motionStates);
|
||||||
|
@ -98,16 +96,25 @@ public:
|
||||||
void sendOwnedUpdates(uint32_t numSubsteps);
|
void sendOwnedUpdates(uint32_t numSubsteps);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SetOfEntities _entitiesToAddToPhysics;
|
void buildMotionStatesForEntitiesThatNeedThem();
|
||||||
|
|
||||||
|
class ShapeRequest {
|
||||||
|
public:
|
||||||
|
ShapeRequest() { }
|
||||||
|
ShapeRequest(const EntityItemPointer& e) : entity(e) { }
|
||||||
|
bool operator<(const ShapeRequest& other) const { return entity.get() < other.entity.get(); }
|
||||||
|
bool operator==(const ShapeRequest& other) const { return entity.get() == other.entity.get(); }
|
||||||
|
EntityItemPointer entity { nullptr };
|
||||||
|
mutable uint64_t shapeHash { 0 };
|
||||||
|
};
|
||||||
|
SetOfEntities _entitiesToAddToPhysics; // we could also call this: _entitiesThatNeedMotionStates
|
||||||
SetOfEntities _entitiesToRemoveFromPhysics;
|
SetOfEntities _entitiesToRemoveFromPhysics;
|
||||||
|
SetOfEntityMotionStates _incomingChanges; // EntityMotionStates changed by external events
|
||||||
VectorOfMotionStates _objectsToDelete;
|
|
||||||
|
|
||||||
SetOfEntityMotionStates _incomingChanges; // EntityMotionStates that have changed from external sources
|
|
||||||
// and need their RigidBodies updated
|
|
||||||
|
|
||||||
SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine
|
SetOfMotionStates _physicalObjects; // MotionStates of entities in PhysicsEngine
|
||||||
|
|
||||||
|
using ShapeRequests = std::set<ShapeRequest>;
|
||||||
|
ShapeRequests _shapeRequests;
|
||||||
|
|
||||||
PhysicsEnginePointer _physicsEngine = nullptr;
|
PhysicsEnginePointer _physicsEngine = nullptr;
|
||||||
EntityEditPacketSender* _entityPacketSender = nullptr;
|
EntityEditPacketSender* _entityPacketSender = nullptr;
|
||||||
|
|
||||||
|
@ -117,6 +124,7 @@ private:
|
||||||
workload::SpacePointer _space;
|
workload::SpacePointer _space;
|
||||||
uint64_t _nextBidExpiry;
|
uint64_t _nextBidExpiry;
|
||||||
uint32_t _lastStepSendPackets { 0 };
|
uint32_t _lastStepSendPackets { 0 };
|
||||||
|
uint32_t _lastWorkDeliveryCount { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -178,8 +178,6 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
||||||
int32_t group, mask;
|
int32_t group, mask;
|
||||||
motionState->computeCollisionGroupAndMask(group, mask);
|
motionState->computeCollisionGroupAndMask(group, mask);
|
||||||
_dynamicsWorld->addRigidBody(body, group, mask);
|
_dynamicsWorld->addRigidBody(body, group, mask);
|
||||||
|
|
||||||
motionState->clearIncomingDirtyFlags();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<EntityDynamicPointer> PhysicsEngine::removeDynamicsForBody(btRigidBody* body) {
|
QList<EntityDynamicPointer> PhysicsEngine::removeDynamicsForBody(btRigidBody* body) {
|
||||||
|
@ -252,6 +250,7 @@ void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) {
|
||||||
}
|
}
|
||||||
object->clearIncomingDirtyFlags();
|
object->clearIncomingDirtyFlags();
|
||||||
}
|
}
|
||||||
|
_activeStaticBodies.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) {
|
void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) {
|
||||||
|
@ -260,35 +259,6 @@ void PhysicsEngine::addObjects(const VectorOfMotionStates& objects) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& objects) {
|
|
||||||
VectorOfMotionStates stillNeedChange;
|
|
||||||
for (auto object : objects) {
|
|
||||||
uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS;
|
|
||||||
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
|
|
||||||
if (object->handleHardAndEasyChanges(flags, this)) {
|
|
||||||
object->clearIncomingDirtyFlags();
|
|
||||||
} else {
|
|
||||||
stillNeedChange.push_back(object);
|
|
||||||
}
|
|
||||||
} else if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
|
||||||
object->handleEasyChanges(flags);
|
|
||||||
object->clearIncomingDirtyFlags();
|
|
||||||
}
|
|
||||||
if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) {
|
|
||||||
_activeStaticBodies.insert(object->getRigidBody());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// active static bodies have changed (in an Easy way) and need their Aabbs updated
|
|
||||||
// but we've configured Bullet to NOT update them automatically (for improved performance)
|
|
||||||
// so we must do it ourselves
|
|
||||||
std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin();
|
|
||||||
while (itr != _activeStaticBodies.end()) {
|
|
||||||
_dynamicsWorld->updateSingleAabb(*itr);
|
|
||||||
++itr;
|
|
||||||
}
|
|
||||||
return stillNeedChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhysicsEngine::reinsertObject(ObjectMotionState* object) {
|
void PhysicsEngine::reinsertObject(ObjectMotionState* object) {
|
||||||
// remove object from DynamicsWorld
|
// remove object from DynamicsWorld
|
||||||
bumpAndPruneContacts(object);
|
bumpAndPruneContacts(object);
|
||||||
|
@ -320,7 +290,6 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction)
|
||||||
body->setMotionState(nullptr);
|
body->setMotionState(nullptr);
|
||||||
delete body;
|
delete body;
|
||||||
}
|
}
|
||||||
object->clearIncomingDirtyFlags();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// adds
|
// adds
|
||||||
|
@ -328,34 +297,16 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction)
|
||||||
addObjectToDynamicsWorld(object);
|
addObjectToDynamicsWorld(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
// changes
|
// reinserts
|
||||||
std::vector<ObjectMotionState*> failedChanges;
|
for (auto object : transaction.objectsToReinsert) {
|
||||||
for (auto object : transaction.objectsToChange) {
|
reinsertObject(object);
|
||||||
uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS;
|
|
||||||
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
|
|
||||||
if (object->handleHardAndEasyChanges(flags, this)) {
|
|
||||||
object->clearIncomingDirtyFlags();
|
|
||||||
} else {
|
|
||||||
failedChanges.push_back(object);
|
|
||||||
}
|
|
||||||
} else if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
|
||||||
object->handleEasyChanges(flags);
|
|
||||||
object->clearIncomingDirtyFlags();
|
|
||||||
}
|
|
||||||
if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) {
|
|
||||||
_activeStaticBodies.insert(object->getRigidBody());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// activeStaticBodies have changed (in an Easy way) and need their Aabbs updated
|
|
||||||
// but we've configured Bullet to NOT update them automatically (for improved performance)
|
for (auto object : transaction.activeStaticObjects) {
|
||||||
// so we must do it ourselves
|
btRigidBody* body = object->getRigidBody();
|
||||||
std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin();
|
_dynamicsWorld->updateSingleAabb(body);
|
||||||
while (itr != _activeStaticBodies.end()) {
|
_activeStaticBodies.insert(body);
|
||||||
_dynamicsWorld->updateSingleAabb(*itr);
|
|
||||||
++itr;
|
|
||||||
}
|
}
|
||||||
// we replace objectsToChange with any that failed
|
|
||||||
transaction.objectsToChange.swap(failedChanges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::removeContacts(ObjectMotionState* motionState) {
|
void PhysicsEngine::removeContacts(ObjectMotionState* motionState) {
|
||||||
|
|
|
@ -79,11 +79,13 @@ public:
|
||||||
void clear() {
|
void clear() {
|
||||||
objectsToRemove.clear();
|
objectsToRemove.clear();
|
||||||
objectsToAdd.clear();
|
objectsToAdd.clear();
|
||||||
objectsToChange.clear();
|
objectsToReinsert.clear();
|
||||||
|
activeStaticObjects.clear();
|
||||||
}
|
}
|
||||||
std::vector<ObjectMotionState*> objectsToRemove;
|
std::vector<ObjectMotionState*> objectsToRemove;
|
||||||
std::vector<ObjectMotionState*> objectsToAdd;
|
std::vector<ObjectMotionState*> objectsToAdd;
|
||||||
std::vector<ObjectMotionState*> objectsToChange;
|
std::vector<ObjectMotionState*> objectsToReinsert;
|
||||||
|
std::vector<ObjectMotionState*> activeStaticObjects;
|
||||||
};
|
};
|
||||||
|
|
||||||
PhysicsEngine(const glm::vec3& offset);
|
PhysicsEngine(const glm::vec3& offset);
|
||||||
|
@ -97,7 +99,7 @@ public:
|
||||||
void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown
|
void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown
|
||||||
|
|
||||||
void addObjects(const VectorOfMotionStates& objects);
|
void addObjects(const VectorOfMotionStates& objects);
|
||||||
VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects);
|
void changeObjects(const VectorOfMotionStates& objects);
|
||||||
void reinsertObject(ObjectMotionState* object);
|
void reinsertObject(ObjectMotionState* object);
|
||||||
|
|
||||||
void processTransaction(Transaction& transaction);
|
void processTransaction(Transaction& transaction);
|
||||||
|
|
|
@ -293,6 +293,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
|
||||||
radiuses.push_back(sphereData.w);
|
radiuses.push_back(sphereData.w);
|
||||||
}
|
}
|
||||||
shape = new btMultiSphereShape(positions.data(), radiuses.data(), (int)positions.size());
|
shape = new btMultiSphereShape(positions.data(), radiuses.data(), (int)positions.size());
|
||||||
|
const float MULTI_SPHERE_MARGIN = 0.001f;
|
||||||
|
shape->setMargin(MULTI_SPHERE_MARGIN);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SHAPE_TYPE_ELLIPSOID: {
|
case SHAPE_TYPE_ELLIPSOID: {
|
||||||
|
@ -433,6 +435,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (shape) {
|
if (shape) {
|
||||||
if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) {
|
if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) {
|
||||||
|
@ -457,6 +461,8 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
|
||||||
shape = compound;
|
shape = compound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: warn about this case
|
||||||
}
|
}
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
@ -481,3 +487,8 @@ void ShapeFactory::deleteShape(const btCollisionShape* shape) {
|
||||||
}
|
}
|
||||||
delete nonConstShape;
|
delete nonConstShape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShapeFactory::Worker::run() {
|
||||||
|
shape = ShapeFactory::createShapeFromInfo(shapeInfo);
|
||||||
|
emit submitWork(this);
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include <btBulletDynamicsCommon.h>
|
#include <btBulletDynamicsCommon.h>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtCore/QRunnable>
|
||||||
|
|
||||||
#include <ShapeInfo.h>
|
#include <ShapeInfo.h>
|
||||||
|
|
||||||
|
@ -22,6 +24,17 @@
|
||||||
namespace ShapeFactory {
|
namespace ShapeFactory {
|
||||||
const btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
const btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||||
void deleteShape(const btCollisionShape* shape);
|
void deleteShape(const btCollisionShape* shape);
|
||||||
|
|
||||||
|
class Worker : public QObject, public QRunnable {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Worker(const ShapeInfo& info) : shapeInfo(info), shape(nullptr) {}
|
||||||
|
void run() override;
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
|
const btCollisionShape* shape;
|
||||||
|
signals:
|
||||||
|
void submitWork(Worker*);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ShapeFactory_h
|
#endif // hifi_ShapeFactory_h
|
||||||
|
|
|
@ -12,15 +12,15 @@
|
||||||
#include "ShapeManager.h"
|
#include "ShapeManager.h"
|
||||||
|
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
#include <QThreadPool>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
#include "ShapeFactory.h"
|
|
||||||
|
|
||||||
const int MAX_RING_SIZE = 256;
|
const int MAX_RING_SIZE = 256;
|
||||||
|
|
||||||
ShapeManager::ShapeManager() {
|
ShapeManager::ShapeManager() {
|
||||||
_garbageRing.reserve(MAX_RING_SIZE);
|
_garbageRing.reserve(MAX_RING_SIZE);
|
||||||
|
_nextOrphanExpiry = std::chrono::steady_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeManager::~ShapeManager() {
|
ShapeManager::~ShapeManager() {
|
||||||
|
@ -30,6 +30,10 @@ ShapeManager::~ShapeManager() {
|
||||||
ShapeFactory::deleteShape(shapeRef->shape);
|
ShapeFactory::deleteShape(shapeRef->shape);
|
||||||
}
|
}
|
||||||
_shapeMap.clear();
|
_shapeMap.clear();
|
||||||
|
if (_deadWorker) {
|
||||||
|
delete _deadWorker;
|
||||||
|
_deadWorker = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
|
@ -42,17 +46,84 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
shapeRef->refCount++;
|
shapeRef->refCount++;
|
||||||
return shapeRef->shape;
|
return shapeRef->shape;
|
||||||
}
|
}
|
||||||
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
const btCollisionShape* shape = nullptr;
|
||||||
if (shape) {
|
if (info.getType() == SHAPE_TYPE_STATIC_MESH) {
|
||||||
ShapeReference newRef;
|
uint64_t hash = info.getHash();
|
||||||
newRef.refCount = 1;
|
const auto itr = std::find(_pendingMeshShapes.begin(), _pendingMeshShapes.end(), hash);
|
||||||
newRef.shape = shape;
|
if (itr == _pendingMeshShapes.end()) {
|
||||||
newRef.key = info.getHash();
|
// start a worker
|
||||||
_shapeMap.insert(hashKey, newRef);
|
_pendingMeshShapes.push_back(hash);
|
||||||
|
++_workRequestCount;
|
||||||
|
// try to recycle old deadWorker
|
||||||
|
ShapeFactory::Worker* worker = _deadWorker;
|
||||||
|
if (!worker) {
|
||||||
|
worker = new ShapeFactory::Worker(info);
|
||||||
|
} else {
|
||||||
|
worker->shapeInfo = info;
|
||||||
|
_deadWorker = nullptr;
|
||||||
|
}
|
||||||
|
// we will delete worker manually later
|
||||||
|
worker->setAutoDelete(false);
|
||||||
|
QObject::connect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork);
|
||||||
|
QThreadPool::globalInstance()->start(worker);
|
||||||
|
}
|
||||||
|
// else we're still waiting for the shape to be created on another thread
|
||||||
|
} else {
|
||||||
|
shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
|
if (shape) {
|
||||||
|
ShapeReference newRef;
|
||||||
|
newRef.refCount = 1;
|
||||||
|
newRef.shape = shape;
|
||||||
|
newRef.key = info.getHash();
|
||||||
|
_shapeMap.insert(hashKey, newRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const btCollisionShape* ShapeManager::getShapeByKey(uint64_t key) {
|
||||||
|
HashKey hashKey(key);
|
||||||
|
ShapeReference* shapeRef = _shapeMap.find(hashKey);
|
||||||
|
if (shapeRef) {
|
||||||
|
shapeRef->refCount++;
|
||||||
|
return shapeRef->shape;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShapeManager::hasShapeWithKey(uint64_t key) const {
|
||||||
|
HashKey hashKey(key);
|
||||||
|
const ShapeReference* shapeRef = _shapeMap.find(hashKey);
|
||||||
|
return (bool)shapeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeManager::addToGarbage(uint64_t key) {
|
||||||
|
// look for existing entry in _garbageRing
|
||||||
|
int32_t ringSize = (int32_t)(_garbageRing.size());
|
||||||
|
for (int32_t i = 0; i < ringSize; ++i) {
|
||||||
|
int32_t j = (_ringIndex + ringSize) % ringSize;
|
||||||
|
if (_garbageRing[j] == key) {
|
||||||
|
// already on the list, don't add it again
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ringSize == MAX_RING_SIZE) {
|
||||||
|
// remove one
|
||||||
|
HashKey hashKeyToRemove(_garbageRing[_ringIndex]);
|
||||||
|
ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove);
|
||||||
|
if (shapeRef && shapeRef->refCount == 0) {
|
||||||
|
ShapeFactory::deleteShape(shapeRef->shape);
|
||||||
|
_shapeMap.remove(hashKeyToRemove);
|
||||||
|
}
|
||||||
|
// replace at _ringIndex and advance
|
||||||
|
_garbageRing[_ringIndex] = key;
|
||||||
|
_ringIndex = (_ringIndex + 1) % ringSize;
|
||||||
|
} else {
|
||||||
|
// add one
|
||||||
|
_garbageRing.push_back(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// private helper method
|
// private helper method
|
||||||
bool ShapeManager::releaseShapeByKey(uint64_t key) {
|
bool ShapeManager::releaseShapeByKey(uint64_t key) {
|
||||||
HashKey hashKey(key);
|
HashKey hashKey(key);
|
||||||
|
@ -61,30 +132,7 @@ bool ShapeManager::releaseShapeByKey(uint64_t key) {
|
||||||
if (shapeRef->refCount > 0) {
|
if (shapeRef->refCount > 0) {
|
||||||
shapeRef->refCount--;
|
shapeRef->refCount--;
|
||||||
if (shapeRef->refCount == 0) {
|
if (shapeRef->refCount == 0) {
|
||||||
// look for existing entry in _garbageRing
|
addToGarbage(key);
|
||||||
int32_t ringSize = (int32_t)(_garbageRing.size());
|
|
||||||
for (int32_t i = 0; i < ringSize; ++i) {
|
|
||||||
int32_t j = (_ringIndex + ringSize) % ringSize;
|
|
||||||
if (_garbageRing[j] == key) {
|
|
||||||
// already on the list, don't add it again
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ringSize == MAX_RING_SIZE) {
|
|
||||||
// remove one
|
|
||||||
HashKey hashKeyToRemove(_garbageRing[_ringIndex]);
|
|
||||||
ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove);
|
|
||||||
if (shapeRef && shapeRef->refCount == 0) {
|
|
||||||
ShapeFactory::deleteShape(shapeRef->shape);
|
|
||||||
_shapeMap.remove(hashKeyToRemove);
|
|
||||||
}
|
|
||||||
// replace at _ringIndex and advance
|
|
||||||
_garbageRing[_ringIndex] = key;
|
|
||||||
_ringIndex = (_ringIndex + 1) % ringSize;
|
|
||||||
} else {
|
|
||||||
// add one
|
|
||||||
_garbageRing.push_back(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,3 +201,78 @@ bool ShapeManager::hasShape(const btCollisionShape* shape) const {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// slot: called when ShapeFactory::Worker is done building shape
|
||||||
|
void ShapeManager::acceptWork(ShapeFactory::Worker* worker) {
|
||||||
|
auto itr = std::find(_pendingMeshShapes.begin(), _pendingMeshShapes.end(), worker->shapeInfo.getHash());
|
||||||
|
if (itr == _pendingMeshShapes.end()) {
|
||||||
|
// we've received a shape but don't remember asking for it
|
||||||
|
// (should not fall in here, but if we do: delete the unwanted shape)
|
||||||
|
if (worker->shape) {
|
||||||
|
ShapeFactory::deleteShape(worker->shape);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// clear pending status
|
||||||
|
*itr = _pendingMeshShapes.back();
|
||||||
|
_pendingMeshShapes.pop_back();
|
||||||
|
|
||||||
|
// cache the new shape
|
||||||
|
if (worker->shape) {
|
||||||
|
ShapeReference newRef;
|
||||||
|
// refCount is zero because nothing is using the shape yet
|
||||||
|
newRef.refCount = 0;
|
||||||
|
newRef.shape = worker->shape;
|
||||||
|
newRef.key = worker->shapeInfo.getHash();
|
||||||
|
HashKey hashKey(newRef.key);
|
||||||
|
_shapeMap.insert(hashKey, newRef);
|
||||||
|
|
||||||
|
// This shape's refCount is zero because an object requested it but is not yet using it. We expect it to be
|
||||||
|
// used later but there is a possibility it will never be used (e.g. the object that wanted it was removed
|
||||||
|
// before the shape could be added, or has changed its mind and now wants a different shape).
|
||||||
|
// Normally zero refCount shapes belong on _garbageRing for possible cleanup but we don't want to add it there
|
||||||
|
// because it might get reaped too soon. So we add it to _orphans to check later. If it still has zero
|
||||||
|
// refCount on expiry we will move it to _garbageRing.
|
||||||
|
const int64_t SHAPE_EXPIRY = USECS_PER_SECOND;
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
auto newExpiry = now + std::chrono::microseconds(SHAPE_EXPIRY);
|
||||||
|
if (_nextOrphanExpiry < now) {
|
||||||
|
_nextOrphanExpiry = newExpiry;
|
||||||
|
// check for expired orphan shapes
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < _orphans.size()) {
|
||||||
|
auto expiry = _orphans[i].expiry;
|
||||||
|
if (expiry < now) {
|
||||||
|
uint64_t key = _orphans[i].key;
|
||||||
|
HashKey hashKey(key);
|
||||||
|
ShapeReference* shapeRef = _shapeMap.find(hashKey);
|
||||||
|
if (shapeRef) {
|
||||||
|
if (shapeRef->refCount == 0) {
|
||||||
|
// shape unused after expiry
|
||||||
|
addToGarbage(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_orphans[i] = _orphans.back();
|
||||||
|
_orphans.pop_back();
|
||||||
|
} else {
|
||||||
|
if (expiry < _nextOrphanExpiry) {
|
||||||
|
_nextOrphanExpiry = expiry;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_orphans.push_back(KeyExpiry(newRef.key, newExpiry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disconnect(worker, &ShapeFactory::Worker::submitWork, this, &ShapeManager::acceptWork);
|
||||||
|
|
||||||
|
if (_deadWorker) {
|
||||||
|
// delete the previous deadWorker manually
|
||||||
|
delete _deadWorker;
|
||||||
|
}
|
||||||
|
// save this dead worker for later
|
||||||
|
worker->shapeInfo.clear();
|
||||||
|
worker->shape = nullptr;
|
||||||
|
_deadWorker = worker;
|
||||||
|
++_workDeliveryCount;
|
||||||
|
}
|
||||||
|
|
|
@ -12,13 +12,17 @@
|
||||||
#ifndef hifi_ShapeManager_h
|
#ifndef hifi_ShapeManager_h
|
||||||
#define hifi_ShapeManager_h
|
#define hifi_ShapeManager_h
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
#include <btBulletDynamicsCommon.h>
|
#include <btBulletDynamicsCommon.h>
|
||||||
#include <LinearMath/btHashMap.h>
|
#include <LinearMath/btHashMap.h>
|
||||||
|
|
||||||
#include <ShapeInfo.h>
|
#include <ShapeInfo.h>
|
||||||
|
|
||||||
|
#include "ShapeFactory.h"
|
||||||
#include "HashKey.h"
|
#include "HashKey.h"
|
||||||
|
|
||||||
// The ShapeManager handles the ref-counting on shared shapes:
|
// The ShapeManager handles the ref-counting on shared shapes:
|
||||||
|
@ -44,7 +48,8 @@
|
||||||
// entries that still have zero ref-count.
|
// entries that still have zero ref-count.
|
||||||
|
|
||||||
|
|
||||||
class ShapeManager {
|
class ShapeManager : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ShapeManager();
|
ShapeManager();
|
||||||
|
@ -52,6 +57,8 @@ public:
|
||||||
|
|
||||||
/// \return pointer to shape
|
/// \return pointer to shape
|
||||||
const btCollisionShape* getShape(const ShapeInfo& info);
|
const btCollisionShape* getShape(const ShapeInfo& info);
|
||||||
|
const btCollisionShape* getShapeByKey(uint64_t key);
|
||||||
|
bool hasShapeWithKey(uint64_t key) const;
|
||||||
|
|
||||||
/// \return true if shape was found and released
|
/// \return true if shape was found and released
|
||||||
bool releaseShape(const btCollisionShape* shape);
|
bool releaseShape(const btCollisionShape* shape);
|
||||||
|
@ -64,8 +71,14 @@ public:
|
||||||
int getNumReferences(const ShapeInfo& info) const;
|
int getNumReferences(const ShapeInfo& info) const;
|
||||||
int getNumReferences(const btCollisionShape* shape) const;
|
int getNumReferences(const btCollisionShape* shape) const;
|
||||||
bool hasShape(const btCollisionShape* shape) const;
|
bool hasShape(const btCollisionShape* shape) const;
|
||||||
|
uint32_t getWorkRequestCount() const { return _workRequestCount; }
|
||||||
|
uint32_t getWorkDeliveryCount() const { return _workDeliveryCount; }
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void acceptWork(ShapeFactory::Worker* worker);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void addToGarbage(uint64_t key);
|
||||||
bool releaseShapeByKey(uint64_t key);
|
bool releaseShapeByKey(uint64_t key);
|
||||||
|
|
||||||
class ShapeReference {
|
class ShapeReference {
|
||||||
|
@ -76,10 +89,24 @@ private:
|
||||||
ShapeReference() : refCount(0), shape(nullptr) {}
|
ShapeReference() : refCount(0), shape(nullptr) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
|
||||||
|
class KeyExpiry {
|
||||||
|
public:
|
||||||
|
KeyExpiry(uint64_t k, std::chrono::time_point<std::chrono::steady_clock> e) : expiry(e), key(k) {}
|
||||||
|
TimePoint expiry;
|
||||||
|
uint64_t key;
|
||||||
|
};
|
||||||
|
|
||||||
// btHashMap is required because it supports memory alignment of the btCollisionShapes
|
// btHashMap is required because it supports memory alignment of the btCollisionShapes
|
||||||
btHashMap<HashKey, ShapeReference> _shapeMap;
|
btHashMap<HashKey, ShapeReference> _shapeMap;
|
||||||
std::vector<uint64_t> _garbageRing;
|
std::vector<uint64_t> _garbageRing;
|
||||||
|
std::vector<uint64_t> _pendingMeshShapes;
|
||||||
|
std::vector<KeyExpiry> _orphans;
|
||||||
|
ShapeFactory::Worker* _deadWorker { nullptr };
|
||||||
|
TimePoint _nextOrphanExpiry;
|
||||||
uint32_t _ringIndex { 0 };
|
uint32_t _ringIndex { 0 };
|
||||||
|
std::atomic_uint _workRequestCount { 0 };
|
||||||
|
std::atomic_uint _workDeliveryCount { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ShapeManager_h
|
#endif // hifi_ShapeManager_h
|
||||||
|
|
|
@ -51,15 +51,15 @@ float HashKey::getNumQuantizedValuesPerMeter() {
|
||||||
return QUANTIZED_VALUES_PER_METER;
|
return QUANTIZED_VALUES_PER_METER;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashKey::hashUint64(uint64_t data) {
|
void HashKey::Hasher::hashUint64(uint64_t data) {
|
||||||
_hash += squirrel3_64(data, ++_hashCount);
|
_hash += squirrel3_64(data, ++_hashCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashKey::hashFloat(float data) {
|
void HashKey::Hasher::hashFloat(float data) {
|
||||||
_hash += squirrel3_64((uint64_t)((int64_t)(data * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
_hash += squirrel3_64((uint64_t)((int64_t)(data * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashKey::hashVec3(const glm::vec3& data) {
|
void HashKey::Hasher::hashVec3(const glm::vec3& data) {
|
||||||
_hash += squirrel3_64((uint64_t)((int64_t)(data[0] * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
_hash += squirrel3_64((uint64_t)((int64_t)(data[0] * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
||||||
_hash *= squirrel3_64((uint64_t)((int64_t)(data[1] * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
_hash *= squirrel3_64((uint64_t)((int64_t)(data[1] * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
||||||
_hash ^= squirrel3_64((uint64_t)((int64_t)(data[2] * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
_hash ^= squirrel3_64((uint64_t)((int64_t)(data[2] * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
||||||
|
|
|
@ -30,6 +30,18 @@
|
||||||
|
|
||||||
class HashKey {
|
class HashKey {
|
||||||
public:
|
public:
|
||||||
|
class Hasher {
|
||||||
|
public:
|
||||||
|
Hasher() {}
|
||||||
|
void hashUint64(uint64_t data);
|
||||||
|
void hashFloat(float data);
|
||||||
|
void hashVec3(const glm::vec3& data);
|
||||||
|
uint64_t getHash64() const { return _hash; }
|
||||||
|
private:
|
||||||
|
uint64_t _hash { 0 };
|
||||||
|
uint8_t _hashCount { 0 };
|
||||||
|
};
|
||||||
|
|
||||||
static float getNumQuantizedValuesPerMeter();
|
static float getNumQuantizedValuesPerMeter();
|
||||||
|
|
||||||
HashKey() {}
|
HashKey() {}
|
||||||
|
@ -39,16 +51,9 @@ public:
|
||||||
bool equals(const HashKey& other) const { return _hash == other._hash; }
|
bool equals(const HashKey& other) const { return _hash == other._hash; }
|
||||||
int32_t getHash() const { return (int32_t)((uint32_t)_hash); }
|
int32_t getHash() const { return (int32_t)((uint32_t)_hash); }
|
||||||
|
|
||||||
// These methods for accumulating a hash.
|
|
||||||
void hashUint64(uint64_t data);
|
|
||||||
void hashFloat(float data);
|
|
||||||
void hashVec3(const glm::vec3& data);
|
|
||||||
|
|
||||||
uint64_t getHash64() const { return _hash; }
|
uint64_t getHash64() const { return _hash; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _hash { 0 };
|
uint64_t _hash { 0 };
|
||||||
uint8_t _hashCount { 0 };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_HashKey_h
|
#endif // hifi_HashKey_h
|
||||||
|
|
|
@ -273,18 +273,18 @@ float ShapeInfo::computeVolume() const {
|
||||||
uint64_t ShapeInfo::getHash() const {
|
uint64_t 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 (_hash64 == 0 && _type != SHAPE_TYPE_NONE) {
|
if (_hash64 == 0 && _type != SHAPE_TYPE_NONE) {
|
||||||
HashKey hashKey;
|
HashKey::Hasher hasher;
|
||||||
// The key is not yet cached therefore we must compute it.
|
// The key is not yet cached therefore we must compute it.
|
||||||
|
|
||||||
hashKey.hashUint64((uint64_t)_type);
|
hasher.hashUint64((uint64_t)_type);
|
||||||
if (_type == SHAPE_TYPE_MULTISPHERE) {
|
if (_type == SHAPE_TYPE_MULTISPHERE) {
|
||||||
for (auto &sphereData : _sphereCollection) {
|
for (auto &sphereData : _sphereCollection) {
|
||||||
hashKey.hashVec3(glm::vec3(sphereData));
|
hasher.hashVec3(glm::vec3(sphereData));
|
||||||
hashKey.hashFloat(sphereData.w);
|
hasher.hashFloat(sphereData.w);
|
||||||
}
|
}
|
||||||
} else if (_type != SHAPE_TYPE_SIMPLE_HULL) {
|
} else if (_type != SHAPE_TYPE_SIMPLE_HULL) {
|
||||||
hashKey.hashVec3(_halfExtents);
|
hasher.hashVec3(_halfExtents);
|
||||||
hashKey.hashVec3(_offset);
|
hasher.hashVec3(_offset);
|
||||||
} else {
|
} else {
|
||||||
// TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique
|
// TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique
|
||||||
// descriptive string. Shapes that are uniquely described by their type and URL could just put their
|
// descriptive string. Shapes that are uniquely described by their type and URL could just put their
|
||||||
|
@ -294,7 +294,7 @@ uint64_t ShapeInfo::getHash() const {
|
||||||
const int numPoints = (int)points.size();
|
const int numPoints = (int)points.size();
|
||||||
|
|
||||||
for (int i = 0; i < numPoints; ++i) {
|
for (int i = 0; i < numPoints; ++i) {
|
||||||
hashKey.hashVec3(points[i]);
|
hasher.hashVec3(points[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,19 +302,19 @@ uint64_t ShapeInfo::getHash() const {
|
||||||
if (!url.isEmpty()) {
|
if (!url.isEmpty()) {
|
||||||
QByteArray baUrl = url.toLocal8Bit();
|
QByteArray baUrl = url.toLocal8Bit();
|
||||||
uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size());
|
uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size());
|
||||||
hashKey.hashUint64((uint64_t)urlHash);
|
hasher.hashUint64((uint64_t)urlHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||||
uint64_t numHulls = (uint64_t)_pointCollection.size();
|
uint64_t numHulls = (uint64_t)_pointCollection.size();
|
||||||
hashKey.hashUint64(numHulls);
|
hasher.hashUint64(numHulls);
|
||||||
} else if (_type == SHAPE_TYPE_MULTISPHERE) {
|
} else if (_type == SHAPE_TYPE_MULTISPHERE) {
|
||||||
uint64_t numSpheres = (uint64_t)_sphereCollection.size();
|
uint64_t numSpheres = (uint64_t)_sphereCollection.size();
|
||||||
hashKey.hashUint64(numSpheres);
|
hasher.hashUint64(numSpheres);
|
||||||
} else if (_type == SHAPE_TYPE_SIMPLE_HULL) {
|
} else if (_type == SHAPE_TYPE_SIMPLE_HULL) {
|
||||||
hashKey.hashUint64(1);
|
hasher.hashUint64(1);
|
||||||
}
|
}
|
||||||
_hash64 = hashKey.getHash64();
|
_hash64 = hasher.getHash64();
|
||||||
}
|
}
|
||||||
return _hash64;
|
return _hash64;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,17 @@
|
||||||
#ifndef hifi_SimulationFlags_h
|
#ifndef hifi_SimulationFlags_h
|
||||||
#define hifi_SimulationFlags_h
|
#define hifi_SimulationFlags_h
|
||||||
|
|
||||||
|
const uint8_t YIELD_SIMULATION_PRIORITY = 1;
|
||||||
|
const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1;
|
||||||
|
const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
||||||
|
|
||||||
|
const uint8_t SCRIPT_GRAB_SIMULATION_PRIORITY = 128;
|
||||||
|
const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1;
|
||||||
|
|
||||||
|
const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||||
|
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY;
|
||||||
|
|
||||||
|
|
||||||
namespace Simulation {
|
namespace Simulation {
|
||||||
const uint32_t DIRTY_POSITION = 0x0001;
|
const uint32_t DIRTY_POSITION = 0x0001;
|
||||||
const uint32_t DIRTY_ROTATION = 0x0002;
|
const uint32_t DIRTY_ROTATION = 0x0002;
|
||||||
|
@ -29,13 +40,14 @@ namespace Simulation {
|
||||||
const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed
|
const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed
|
||||||
|
|
||||||
// bits 17-32 are reservied for special flags
|
// bits 17-32 are reservied for special flags
|
||||||
const uint32_t SPECIAL_FLAGS_NO_BOOTSTRAPPING = 0x10000;
|
const uint32_t SPECIAL_FLAG_NO_BOOTSTRAPPING = 0x10000;
|
||||||
|
const uint32_t SPECIAL_FLAG_IN_PHYSICS_SIMULATION = 0x20000;
|
||||||
|
|
||||||
const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION;
|
const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION;
|
||||||
const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY;
|
const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY;
|
||||||
const uint32_t SPECIAL_FLAGS = SPECIAL_FLAGS_NO_BOOTSTRAPPING;
|
const uint32_t SPECIAL_FLAGS_MASK = SPECIAL_FLAG_NO_BOOTSTRAPPING | SPECIAL_FLAG_IN_PHYSICS_SIMULATION;
|
||||||
|
|
||||||
const uint32_t DIRTY_FLAGS = DIRTY_POSITION |
|
const uint32_t DIRTY_FLAGS_MASK = DIRTY_POSITION |
|
||||||
DIRTY_ROTATION |
|
DIRTY_ROTATION |
|
||||||
DIRTY_LINEAR_VELOCITY |
|
DIRTY_LINEAR_VELOCITY |
|
||||||
DIRTY_ANGULAR_VELOCITY |
|
DIRTY_ANGULAR_VELOCITY |
|
|
@ -37,8 +37,8 @@ View View::evalFromFrustum(const ViewFrustum& frustum, const glm::vec3& offset)
|
||||||
|
|
||||||
Sphere View::evalRegionSphere(const View& view, float originRadius, float maxDistance) {
|
Sphere View::evalRegionSphere(const View& view, float originRadius, float maxDistance) {
|
||||||
float radius = (maxDistance + originRadius) / 2.0f;
|
float radius = (maxDistance + originRadius) / 2.0f;
|
||||||
float center = radius - originRadius;
|
float distanceToCenter = radius - originRadius;
|
||||||
return Sphere(view.origin + view.direction * center, radius);
|
return Sphere(view.origin + view.direction * distanceToCenter, radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
void View::updateRegionsDefault(View& view) {
|
void View::updateRegionsDefault(View& view) {
|
||||||
|
|
Loading…
Reference in a new issue