mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge pull request #7278 from AndrewMeadows/simulation-priority
adjust simulation priorities
This commit is contained in:
commit
ee1e4b898e
9 changed files with 87 additions and 74 deletions
|
@ -368,7 +368,8 @@ public:
|
|||
|
||||
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
|
||||
|
||||
void flagForOwnership() { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_OWNERSHIP; }
|
||||
void pokeSimulationOwnership() { _dirtyFlags |= Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_POKE; }
|
||||
void grabSimulationOwnership() { _dirtyFlags |= Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB; }
|
||||
void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; }
|
||||
|
||||
bool addAction(EntitySimulation* simulation, EntityActionPointer action);
|
||||
|
|
|
@ -157,8 +157,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
|
||||
// and make note of it now, so we can act on it right away.
|
||||
propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||
entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||
propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
}
|
||||
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
|
@ -321,20 +321,19 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
// TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object
|
||||
// is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update
|
||||
// and instead let the physics simulation decide when to send a terse update. This would remove
|
||||
// the "slide-no-rotate" glitch (and typical a double-update) that we see during the "poke rolling
|
||||
// the "slide-no-rotate" glitch (and typical double-update) that we see during the "poke rolling
|
||||
// balls" test. However, even if we solve this problem we still need to provide a "slerp the visible
|
||||
// proxy toward the true physical position" feature to hide the final glitches in the remote watcher's
|
||||
// simulation.
|
||||
|
||||
if (entity->getSimulationPriority() < SCRIPT_EDIT_SIMULATION_PRIORITY) {
|
||||
if (entity->getSimulationPriority() < SCRIPT_POKE_SIMULATION_PRIORITY) {
|
||||
// we re-assert our simulation ownership at a higher priority
|
||||
properties.setSimulationOwner(myNodeID,
|
||||
glm::max(entity->getSimulationPriority(), SCRIPT_EDIT_SIMULATION_PRIORITY));
|
||||
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
}
|
||||
} else {
|
||||
// we make a bid for simulation ownership
|
||||
properties.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||
entity->flagForOwnership();
|
||||
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||
entity->pokeSimulationOwnership();
|
||||
}
|
||||
}
|
||||
if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) {
|
||||
|
@ -806,11 +805,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
|||
return false;
|
||||
}
|
||||
success = entity->addAction(simulation, action);
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
if (entity->getSimulatorID() != myNodeID) {
|
||||
entity->flagForOwnership();
|
||||
}
|
||||
entity->grabSimulationOwnership();
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
if (success) {
|
||||
|
@ -824,11 +819,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid&
|
|||
return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
bool success = entity->updateAction(simulation, actionID, arguments);
|
||||
if (success) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
if (entity->getSimulatorID() != myNodeID) {
|
||||
entity->flagForOwnership();
|
||||
}
|
||||
entity->grabSimulationOwnership();
|
||||
}
|
||||
return success;
|
||||
});
|
||||
|
@ -838,6 +829,10 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid&
|
|||
bool success = false;
|
||||
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
|
||||
success = entity->removeAction(simulation, actionID);
|
||||
if (success) {
|
||||
// reduce from grab to poke
|
||||
entity->pokeSimulationOwnership();
|
||||
}
|
||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||
});
|
||||
return success;
|
||||
|
|
|
@ -25,10 +25,13 @@ namespace Simulation {
|
|||
const uint32_t DIRTY_UPDATEABLE = 0x0200;
|
||||
const uint32_t DIRTY_MATERIAL = 0x00400;
|
||||
const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine
|
||||
const uint32_t DIRTY_SIMULATOR_OWNERSHIP = 0x1000; // should claim simulator ownership
|
||||
const uint32_t DIRTY_SIMULATOR_ID = 0x2000; // the simulatorID has changed
|
||||
const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed
|
||||
const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_POKE = 0x2000; // bid for simulation ownership at "poke"
|
||||
const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB = 0x4000; // bid for simulation ownership at "grab"
|
||||
|
||||
const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION;
|
||||
const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY;
|
||||
const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = DIRTY_SIMULATION_OWNERSHIP_FOR_POKE | DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB;
|
||||
};
|
||||
|
||||
#endif // hifi_SimulationFlags_h
|
||||
|
|
|
@ -70,13 +70,15 @@ bool SimulationOwner::setID(const QUuid& id) {
|
|||
}
|
||||
|
||||
bool SimulationOwner::set(const QUuid& id, quint8 priority) {
|
||||
uint8_t oldPriority = _priority;
|
||||
setPriority(priority);
|
||||
return setID(id);
|
||||
return setID(id) || oldPriority != _priority;
|
||||
}
|
||||
|
||||
bool SimulationOwner::set(const SimulationOwner& owner) {
|
||||
uint8_t oldPriority = _priority;
|
||||
setPriority(owner._priority);
|
||||
return setID(owner._id);
|
||||
return setID(owner._id) || oldPriority != _priority;
|
||||
}
|
||||
|
||||
void SimulationOwner::updateExpiry() {
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
#include <SharedUtil.h>
|
||||
#include <UUID.h>
|
||||
|
||||
const quint8 ZERO_SIMULATION_PRIORITY = 0x00;
|
||||
|
||||
// Simulation observers will bid to simulate unowned active objects at the lowest possible priority
|
||||
// which is VOLUNTEER. If the server accepts a VOLUNTEER bid it will automatically bump it
|
||||
// to RECRUIT priority so that other volunteers don't accidentally take over.
|
||||
|
@ -27,11 +25,12 @@ const quint8 VOLUNTEER_SIMULATION_PRIORITY = 0x01;
|
|||
const quint8 RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
||||
|
||||
// When poking objects with scripts an observer will bid at SCRIPT_EDIT priority.
|
||||
const quint8 SCRIPT_EDIT_SIMULATION_PRIORITY = 0x80;
|
||||
const quint8 SCRIPT_GRAB_SIMULATION_PRIORITY = 0x80;
|
||||
const quint8 SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1;
|
||||
|
||||
// PERSONAL priority (needs a better name) is the level at which a simulation observer will bid for
|
||||
// objects that collide its MyAvatar.
|
||||
const quint8 PERSONAL_SIMULATION_PRIORITY = SCRIPT_EDIT_SIMULATION_PRIORITY - 1;
|
||||
// PERSONAL priority (needs a better name) is the level at which a simulation observer owns its own avatar
|
||||
// which really just means: things that collide with it will be bid at a priority level one lower
|
||||
const quint8 PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||
|
||||
|
||||
class SimulationOwner {
|
||||
|
|
|
@ -65,7 +65,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
_loopsWithoutOwner(0),
|
||||
_accelerationNearlyGravityCount(0),
|
||||
_numInactiveUpdates(1),
|
||||
_outgoingPriority(ZERO_SIMULATION_PRIORITY)
|
||||
_outgoingPriority(0)
|
||||
{
|
||||
_type = MOTIONSTATE_TYPE_ENTITY;
|
||||
assert(_entity);
|
||||
|
@ -103,33 +103,36 @@ bool EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
|||
if (_entity->getSimulatorID().isNull()) {
|
||||
// simulation ownership has been removed by an external simulator
|
||||
if (glm::length2(_entity->getVelocity()) == 0.0f) {
|
||||
// this object is coming to rest --> clear the ACTIVATION flag and outgoing priority
|
||||
// this object is coming to rest --> clear the ACTIVATION flag and _outgoingPriority
|
||||
flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||
_body->setActivationState(WANTS_DEACTIVATION);
|
||||
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
||||
_loopsWithoutOwner = 0;
|
||||
_outgoingPriority = 0;
|
||||
} else {
|
||||
// unowned object is still moving --> we should volunteer to own it
|
||||
// disowned object is still moving --> start timer for ownership bid
|
||||
// TODO? put a delay in here proportional to distance from object?
|
||||
setOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
_loopsWithoutOwner = LOOPS_FOR_SIMULATION_ORPHAN;
|
||||
_nextOwnershipBid = 0;
|
||||
upgradeOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
}
|
||||
_loopsWithoutOwner = 0;
|
||||
} else if (_entity->getSimulatorID() == Physics::getSessionUUID()) {
|
||||
// we just inherited ownership, make sure our desired priority matches what we have
|
||||
upgradeOutgoingPriority(_entity->getSimulationPriority());
|
||||
} else {
|
||||
// this entity's simulation is owned by someone, so we push its ownership expiry into the future
|
||||
_outgoingPriority = 0;
|
||||
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
if (Physics::getSessionUUID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) {
|
||||
// either we already own the simulation or our old outgoing priority momentarily looses to current owner
|
||||
// so we clear it
|
||||
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flags & Simulation::DIRTY_SIMULATOR_OWNERSHIP) {
|
||||
// The DIRTY_SIMULATOR_OWNERSHIP bit really means "we should bid for ownership at SCRIPT priority".
|
||||
// Since that bit is set there must be a local script that is updating the physics properties of the objects
|
||||
// therefore we upgrade _outgoingPriority to trigger a bid for ownership.
|
||||
setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||
if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) {
|
||||
// The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bits really mean "we should bid for ownership because
|
||||
// a local script has been changing physics properties, or we should adjust our own ownership priority".
|
||||
// The desired priority is determined by which bits were set.
|
||||
if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB) {
|
||||
_outgoingPriority = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||
} else {
|
||||
_outgoingPriority = SCRIPT_POKE_SIMULATION_PRIORITY;
|
||||
}
|
||||
// reset bid expiry so that we bid ASAP
|
||||
_nextOwnershipBid = 0;
|
||||
}
|
||||
if ((flags & Simulation::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) {
|
||||
_body->activate();
|
||||
|
@ -209,7 +212,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
|||
_loopsWithoutOwner++;
|
||||
|
||||
if (_loopsWithoutOwner > LOOPS_FOR_SIMULATION_ORPHAN && usecTimestampNow() > _nextOwnershipBid) {
|
||||
setOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
upgradeOutgoingPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,7 +246,7 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const {
|
|||
assert(_body);
|
||||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
return _outgoingPriority != ZERO_SIMULATION_PRIORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit();
|
||||
return _outgoingPriority != 0 || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit();
|
||||
}
|
||||
|
||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||
|
@ -298,7 +301,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
|||
}
|
||||
|
||||
if (_entity->actionDataNeedsTransmit()) {
|
||||
setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||
_outgoingPriority = _entity->hasActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : SCRIPT_POKE_SIMULATION_PRIORITY;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -385,16 +388,15 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s
|
|||
|
||||
if (_entity->getSimulatorID() != sessionID) {
|
||||
// we don't own the simulation
|
||||
if (_outgoingPriority != ZERO_SIMULATION_PRIORITY) {
|
||||
// but we would like to own it
|
||||
if (_outgoingPriority < _entity->getSimulationPriority()) {
|
||||
// but our priority loses to remote, so we don't bother trying
|
||||
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
||||
return false;
|
||||
}
|
||||
return usecTimestampNow() > _nextOwnershipBid;
|
||||
bool shouldBid = _outgoingPriority > 0 && // but we would like to own it and
|
||||
usecTimestampNow() > _nextOwnershipBid; // it is time to bid again
|
||||
if (shouldBid && _outgoingPriority < _entity->getSimulationPriority()) {
|
||||
// we are insufficiently interested so clear our interest
|
||||
// and reset the bid expiry
|
||||
_outgoingPriority = 0;
|
||||
_nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
}
|
||||
return false;
|
||||
return shouldBid;
|
||||
}
|
||||
|
||||
return remoteSimulationOutOfSync(simulationStep);
|
||||
|
@ -495,14 +497,24 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
|
|||
#endif //def WANT_DEBUG
|
||||
|
||||
if (_numInactiveUpdates > 0) {
|
||||
// we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID
|
||||
// but we remember that we do still own it... and rely on the server to tell us that we don't
|
||||
// we own the simulation but the entity has stopped so we tell the server we're clearing simulatorID
|
||||
// but we remember we do still own it... and rely on the server to tell us we don't
|
||||
properties.clearSimulationOwner();
|
||||
_outgoingPriority = ZERO_SIMULATION_PRIORITY;
|
||||
_outgoingPriority = 0;
|
||||
} else if (sessionID != _entity->getSimulatorID()) {
|
||||
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
||||
properties.setSimulationOwner(sessionID, glm::max<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY));
|
||||
_nextOwnershipBid = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||
_outgoingPriority = 0; // reset outgoing priority whenever we bid
|
||||
} else if (_outgoingPriority != _entity->getSimulationPriority()) {
|
||||
// we own the simulation but our desired priority has changed
|
||||
if (_outgoingPriority == 0) {
|
||||
// we should release ownership
|
||||
properties.clearSimulationOwner();
|
||||
} else {
|
||||
// we just need to change the priority
|
||||
properties.setSimulationOwner(sessionID, _outgoingPriority);
|
||||
}
|
||||
}
|
||||
|
||||
EntityItemID id(_entity->getID());
|
||||
|
@ -578,7 +590,8 @@ QUuid EntityMotionState::getSimulatorID() const {
|
|||
}
|
||||
|
||||
void EntityMotionState::bump(uint8_t priority) {
|
||||
setOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
|
||||
assert(priority != 0);
|
||||
upgradeOutgoingPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
|
||||
}
|
||||
|
||||
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
||||
|
@ -640,6 +653,6 @@ void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma
|
|||
_entity->computeCollisionGroupAndFinalMask(group, mask);
|
||||
}
|
||||
|
||||
void EntityMotionState::setOutgoingPriority(uint8_t priority) {
|
||||
void EntityMotionState::upgradeOutgoingPriority(uint8_t priority) {
|
||||
_outgoingPriority = glm::max<uint8_t>(_outgoingPriority, priority);
|
||||
}
|
||||
|
|
|
@ -82,12 +82,12 @@ public:
|
|||
|
||||
virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override;
|
||||
|
||||
// eternal logic can suggest a simuator priority bid for the next outgoing update
|
||||
void setOutgoingPriority(uint8_t priority);
|
||||
|
||||
friend class PhysicalEntitySimulation;
|
||||
|
||||
protected:
|
||||
// changes _outgoingPriority only if priority is larger
|
||||
void upgradeOutgoingPriority(uint8_t priority);
|
||||
|
||||
#ifdef WANT_DEBUG_ENTITY_TREE_LOCKS
|
||||
bool entityTreeIsLocked() const;
|
||||
#endif
|
||||
|
@ -125,7 +125,7 @@ protected:
|
|||
uint8_t _loopsWithoutOwner;
|
||||
uint8_t _accelerationNearlyGravityCount;
|
||||
uint8_t _numInactiveUpdates { 1 };
|
||||
uint8_t _outgoingPriority { ZERO_SIMULATION_PRIORITY };
|
||||
uint8_t _outgoingPriority { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_EntityMotionState_h
|
||||
|
|
|
@ -50,7 +50,7 @@ const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TY
|
|||
Simulation::DIRTY_COLLISION_GROUP);
|
||||
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES |
|
||||
Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL |
|
||||
Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATOR_OWNERSHIP);
|
||||
Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY);
|
||||
|
||||
// These are the set of incoming flags that the PhysicsEngine needs to hear about:
|
||||
const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS |
|
||||
|
|
|
@ -291,8 +291,8 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const
|
|||
// NOTE: we might own the simulation of a kinematic object (A)
|
||||
// but we don't claim ownership of kinematic objects (B) based on collisions here.
|
||||
if (!objectB->isStaticOrKinematicObject() && motionStateB->getSimulatorID() != _sessionID) {
|
||||
quint8 priority = motionStateA ? motionStateA->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
|
||||
motionStateB->bump(priority);
|
||||
quint8 priorityA = motionStateA ? motionStateA->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
|
||||
motionStateB->bump(priorityA);
|
||||
}
|
||||
} else if (motionStateA &&
|
||||
((motionStateB && motionStateB->getSimulatorID() == _sessionID && !objectB->isStaticObject()) ||
|
||||
|
@ -300,8 +300,8 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const
|
|||
// SIMILARLY: we might own the simulation of a kinematic object (B)
|
||||
// but we don't claim ownership of kinematic objects (A) based on collisions here.
|
||||
if (!objectA->isStaticOrKinematicObject() && motionStateA->getSimulatorID() != _sessionID) {
|
||||
quint8 priority = motionStateB ? motionStateB->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
|
||||
motionStateA->bump(priority);
|
||||
quint8 priorityB = motionStateB ? motionStateB->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY;
|
||||
motionStateA->bump(priorityB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue