From f43ba4a68b4806584d89328deb34b6425d4bf34b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Sep 2014 15:28:04 -0700 Subject: [PATCH] first cut at getting entities to collide --- interface/src/Application.cpp | 6 + interface/src/Application.h | 2 + .../entities/src/EntityCollisionSystem.cpp | 351 ++++++++++++++++++ .../entities/src/EntityCollisionSystem.h | 73 ++++ libraries/entities/src/EntityItem.cpp | 81 +++- libraries/entities/src/EntityItem.h | 4 + .../entities/src/EntityItemProperties.cpp | 2 +- libraries/entities/src/EntityItemProperties.h | 22 +- libraries/octree/src/Octree.cpp | 6 +- libraries/particles/src/Particle.cpp | 3 + tests/octree/CMakeLists.txt | 2 +- 11 files changed, 541 insertions(+), 11 deletions(-) create mode 100644 libraries/entities/src/EntityCollisionSystem.cpp create mode 100644 libraries/entities/src/EntityCollisionSystem.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f2ae7a2d8b..974fee4cab 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1822,6 +1822,8 @@ void Application::init() { _entities.init(); _entities.setViewFrustum(getViewFrustum()); + _entityCollisionSystem.init(&_entityEditSender, _entities.getTree(), _voxels.getTree(), &_audio, &_avatarManager); + _entityClipboardRenderer.init(); _entityClipboardRenderer.setViewFrustum(getViewFrustum()); _entityClipboardRenderer.setTree(&_entityClipboard); @@ -2189,6 +2191,10 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("entities"); _entities.update(); // update the models... + { + PerformanceTimer perfTimer("collisions"); + _entityCollisionSystem.update(); // collide the entities... + } } { diff --git a/interface/src/Application.h b/interface/src/Application.h index d17bacd413..49e9cab57a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -482,6 +483,7 @@ private: ParticleCollisionSystem _particleCollisionSystem; EntityTreeRenderer _entities; + EntityCollisionSystem _entityCollisionSystem; EntityTreeRenderer _entityClipboardRenderer; EntityTree _entityClipboard; diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp new file mode 100644 index 0000000000..5a4e18c67a --- /dev/null +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -0,0 +1,351 @@ +// +// EntityCollisionSystem.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 9/23/14. +// Copyright 2013-2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include +#include + +#include "EntityItem.h" +#include "EntityCollisionSystem.h" +#include "EntityEditPacketSender.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" + +const int MAX_COLLISIONS_PER_Entity = 16; + +EntityCollisionSystem::EntityCollisionSystem(EntityEditPacketSender* packetSender, + EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio, + AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_Entity) { + init(packetSender, Entities, voxels, audio, avatars); +} + +void EntityCollisionSystem::init(EntityEditPacketSender* packetSender, + EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio, + AvatarHashMap* avatars) { + _packetSender = packetSender; + _entities = Entities; + _voxels = voxels; + _audio = audio; + _avatars = avatars; +} + +EntityCollisionSystem::~EntityCollisionSystem() { +} + +bool EntityCollisionSystem::updateOperation(OctreeElement* element, void* extraData) { + EntityCollisionSystem* system = static_cast(extraData); + EntityTreeElement* entityTreeElement = static_cast(element); + + // iterate the Entities... + QList& entities = entityTreeElement->getEntities(); + uint16_t numberOfEntities = entities.size(); + for (uint16_t i = 0; i < numberOfEntities; i++) { + EntityItem* entity = entities[i]; + system->checkEntity(entity); + } + + return true; +} + + +void EntityCollisionSystem::update() { + // update all Entities + if (_entities->tryLockForRead()) { + _entities->recurseTreeWithOperation(updateOperation, this); + _entities->unlock(); + } +} + + +void EntityCollisionSystem::checkEntity(EntityItem* entity) { + updateCollisionWithVoxels(entity); + updateCollisionWithEntities(entity); + updateCollisionWithAvatars(entity); +} + +void EntityCollisionSystem::emitGlobalEntityCollisionWithVoxel(EntityItem* entity, + VoxelDetail* voxelDetails, const CollisionInfo& collision) { + EntityItemID entityItemID = entity->getEntityItemID(); + emit EntityCollisionWithVoxel(entityItemID, *voxelDetails, collision); +} + +void EntityCollisionSystem::emitGlobalEntityCollisionWithEntity(EntityItem* entityA, + EntityItem* entityB, const CollisionInfo& collision) { + + EntityItemID idA = entityA->getEntityItemID(); + EntityItemID idB = entityB->getEntityItemID(); + emit EntityCollisionWithEntity(idA, idB, collision); +} + +void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { + glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE); + float radius = entity->getRadius() * (float)(TREE_SCALE); + const float ELASTICITY = 0.4f; + const float DAMPING = 0.05f; + const float COLLISION_FREQUENCY = 0.5f; + CollisionInfo collisionInfo; + collisionInfo._damping = DAMPING; + collisionInfo._elasticity = ELASTICITY; + VoxelDetail* voxelDetails = NULL; + if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) { + + // let the Entities run their collision scripts if they have them + //entity->collisionWithVoxel(voxelDetails, collisionInfo._penetration); + + // findSpherePenetration() only computes the penetration but we also want some other collision info + // so we compute it ourselves here. Note that we must multiply scale by TREE_SCALE when feeding + // the results to systems outside of this octree reference frame. + updateCollisionSound(entity, collisionInfo._penetration, COLLISION_FREQUENCY); + collisionInfo._contactPoint = (float)TREE_SCALE * (entity->getPosition() + entity->getRadius() * glm::normalize(collisionInfo._penetration)); + // let the global script run their collision scripts for Entities if they have them + emitGlobalEntityCollisionWithVoxel(entity, voxelDetails, collisionInfo); + + // we must scale back down to the octree reference frame before updating the Entity properties + collisionInfo._penetration /= (float)(TREE_SCALE); + collisionInfo._contactPoint /= (float)(TREE_SCALE); + entity->applyHardCollision(collisionInfo); + queueEntityPropertiesUpdate(entity); + + delete voxelDetails; // cleanup returned details + } +} + +void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { + glm::vec3 center = entityA->getPosition() * (float)(TREE_SCALE); + float radius = entityA->getRadius() * (float)(TREE_SCALE); + //const float ELASTICITY = 0.4f; + //const float DAMPING = 0.0f; + const float COLLISION_FREQUENCY = 0.5f; + glm::vec3 penetration; + EntityItem* entityB; + if (_entities->findSpherePenetration(center, radius, penetration, (void**)&entityB, Octree::NoLock)) { + // NOTE: 'penetration' is the depth that 'entityA' overlaps 'entityB'. It points from A into B. + +qDebug() << "penetration:" << penetration << "in meters"; + + glm::vec3 penetrationInTreeUnits = penetration / (float)(TREE_SCALE); + +qDebug() << "penetrationInTreeUnits:" << penetrationInTreeUnits; + + // Even if the Entities overlap... when the Entities are already moving appart + // we don't want to count this as a collision. + glm::vec3 relativeVelocity = entityA->getVelocity() - entityB->getVelocity(); + +qDebug() << "A old velocity:" << entityA->getVelocity() * (float)TREE_SCALE << "in meters"; +qDebug() << "B old velocity:" << entityB->getVelocity() * (float)TREE_SCALE << "in meters"; +qDebug() << "relativeVelocity:" << relativeVelocity * (float)TREE_SCALE << "in meters"; + + bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f; + bool doCollisions = true; + + if (doCollisions) { + //entityA->collisionWithEntity(entityB, penetration); + //entityB->collisionWithEntity(entityA, penetration * -1.0f); // the penetration is reversed + + CollisionInfo collision; + collision._penetration = penetration; + // for now the contactPoint is the average between the the two paricle centers + collision._contactPoint = (0.5f * (float)TREE_SCALE) * (entityA->getPosition() + entityB->getPosition()); + //emitGlobalEntityCollisionWithEntity(entityA, entityB, collision); + + glm::vec3 axis = glm::normalize(penetration); + glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; + + float massA = entityA->getMass(); + float massB = entityB->getMass(); + float totalMass = massA + massB; +qDebug() << "massA:" << massA; +qDebug() << "massB:" << massB; +qDebug() << "totalMass:" << totalMass; + + // handle Entity A + +qDebug() << "OLD - VALUES - entityA ----"; +qDebug() << "entityA->getVelocity():" << entityA->getVelocity() << "no scaling"; +qDebug() << "entityA->getVelocity():" << entityA->getVelocity() * (float)TREE_SCALE << "in meters"; +qDebug() << "entityA->getPosition():" << entityA->getPosition() * (float)TREE_SCALE << "in meters"; + + + glm::vec3 newVelocity = entityA->getVelocity() - axialVelocity * (2.0f * massB / totalMass); + glm::vec3 newPosition = entityA->getPosition() - 0.5f * penetrationInTreeUnits; + +qDebug() << "A new Velocity:" << newVelocity * (float)TREE_SCALE << "in meters"; +qDebug() << "A new Position:" << newPosition * (float)TREE_SCALE << "in meters"; + + EntityItemProperties propertiesA = entityA->getProperties(); + +qDebug() << "propertiesA = entityA->getProperties() --- BEFORE SETTING ----"; +qDebug() << " propertiesA.getPosition():" << propertiesA.getPosition() << "in meters"; +qDebug() << " propertiesA.getVelocity():" << propertiesA.getVelocity() << "in meters"; +qDebug() << " propertiesA.getMaximumAACubeInTreeUnits():" << propertiesA.getMaximumAACubeInTreeUnits(); + + EntityItemID idA(entityA->getID()); + propertiesA.setVelocity(newVelocity * (float)TREE_SCALE); + propertiesA.setPosition(newPosition * (float)TREE_SCALE); + +qDebug() << "updateEntity(idA, propertiesA)...."; +qDebug() << " idA:" << idA; +qDebug() << " propertiesA.getPosition():" << propertiesA.getPosition() << "in meters"; +qDebug() << " propertiesA.getVelocity():" << propertiesA.getVelocity() << "in meters"; +qDebug() << " propertiesA.getMaximumAACubeInTreeUnits():" << propertiesA.getMaximumAACubeInTreeUnits(); + + _entities->updateEntity(idA, propertiesA); + +qDebug() << "AFTER updateEntity()...."; +qDebug() << " entityA->getVelocity():" << entityA->getVelocity() << "no scaling"; +qDebug() << " entityA->getPosition():" << entityA->getPosition() * (float)TREE_SCALE << "in meters"; + + + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); + //queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties); + +qDebug() << "OLD - VALUES - entityB ----"; +qDebug() << " entityB->getVelocity():" << entityB->getVelocity() << "no scaling"; +qDebug() << " entityB->getPosition():" << entityB->getPosition() * (float)TREE_SCALE << "in meters"; + + glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * (2.0f * massA / totalMass); + glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits; + +qDebug() << "B new Velocity:" << newVelocityB << "no scaling"; +qDebug() << "B new Position:" << newPositionB * (float)TREE_SCALE << "in meters"; + + EntityItemProperties propertiesB = entityB->getProperties(); + +qDebug() << "propertiesB = entityB->getProperties()"; +qDebug() << " propertiesB.getPosition():" << propertiesB.getPosition() << "not scaled, should be meters"; +qDebug() << " propertiesB.getVelocity():" << propertiesB.getVelocity() << "not scaled, should be ???"; +qDebug() << " propertiesB.getMaximumAACubeInTreeUnits():" << propertiesB.getMaximumAACubeInTreeUnits(); + + EntityItemID idB(entityB->getID()); + propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE); + propertiesB.setPosition(newPositionB * (float)TREE_SCALE); + +qDebug() << "updateEntity(idB, propertiesB)...."; +qDebug() << " idB:" << idB; +qDebug() << " propertiesB.getPosition():" << propertiesB.getPosition(); +qDebug() << " propertiesB.getVelocity():" << propertiesB.getVelocity(); +qDebug() << " propertiesB.getMaximumAACubeInTreeUnits():" << propertiesB.getMaximumAACubeInTreeUnits(); + + _entities->updateEntity(idB, propertiesB); + +qDebug() << "AFTER updateEntity()...."; +qDebug() << " entityB->getVelocity():" << entityB->getVelocity() << "no scaling"; +qDebug() << " entityB->getPosition():" << entityB->getPosition() * (float)TREE_SCALE << "in meters"; + + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); + //_packetSender->releaseQueuedMessages(); + + //updateCollisionSound(entityA, penetration, COLLISION_FREQUENCY); + } + } +} + +void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { + +#if 0 + // Entities that are in hand, don't collide with avatars + if (!_avatars) { + return; + } + + glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE); + float radius = entity->getRadius() * (float)(TREE_SCALE); + const float ELASTICITY = 0.9f; + const float DAMPING = 0.1f; + const float COLLISION_FREQUENCY = 0.5f; + glm::vec3 penetration; + + _collisions.clear(); + foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) { + AvatarData* avatar = avatarPointer.data(); + + float totalRadius = avatar->getBoundingRadius() + radius; + glm::vec3 relativePosition = center - avatar->getPosition(); + if (glm::dot(relativePosition, relativePosition) > (totalRadius * totalRadius)) { + continue; + } + + if (avatar->findSphereCollisions(center, radius, _collisions)) { + int numCollisions = _collisions.size(); + for (int i = 0; i < numCollisions; ++i) { + CollisionInfo* collision = _collisions.getCollision(i); + collision->_damping = DAMPING; + collision->_elasticity = ELASTICITY; + + collision->_addedVelocity /= (float)(TREE_SCALE); + glm::vec3 relativeVelocity = collision->_addedVelocity - entity->getVelocity(); + + if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) { + // only collide when Entity and collision point are moving toward each other + // (doing this prevents some "collision snagging" when Entity penetrates the object) + updateCollisionSound(entity, collision->_penetration, COLLISION_FREQUENCY); + collision->_penetration /= (float)(TREE_SCALE); + entity->applyHardCollision(*collision); + queueEntityPropertiesUpdate(entity); + } + } + } + } + +#endif +} + +void EntityCollisionSystem::queueEntityPropertiesUpdate(EntityItem* entity) { + // queue the result for sending to the Entity server + EntityItemProperties properties = entity->getProperties(); + EntityItemID entityItemID(entity->getID()); + + properties.setPosition(entity->getPosition() * (float)TREE_SCALE); + properties.setVelocity(entity->getVelocity() * (float)TREE_SCALE); + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties); +} + + +void EntityCollisionSystem::updateCollisionSound(EntityItem* Entity, const glm::vec3 &penetration, float frequency) { + + // consider whether to have the collision make a sound + const float AUDIBLE_COLLISION_THRESHOLD = 0.3f; + const float COLLISION_LOUDNESS = 1.f; + const float DURATION_SCALING = 0.004f; + const float NOISE_SCALING = 0.1f; + glm::vec3 velocity = Entity->getVelocity() * (float)(TREE_SCALE); + + /* + // how do we want to handle this?? + // + glm::vec3 gravity = Entity->getGravity() * (float)(TREE_SCALE); + + if (glm::length(gravity) > EPSILON) { + // If gravity is on, remove the effect of gravity on velocity for this + // frame, so that we are not constantly colliding with the surface + velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity); + } + */ + float normalSpeed = glm::dot(velocity, glm::normalize(penetration)); + // NOTE: it is possible for normalSpeed to be NaN at this point + // (sometimes the average penetration of a bunch of voxels is a zero length vector which cannot be normalized) + // however the check below will fail (NaN comparisons always fail) and everything will be fine. + + if (normalSpeed > AUDIBLE_COLLISION_THRESHOLD) { + // Volume is proportional to collision velocity + // Base frequency is modified upward by the angle of the collision + // Noise is a function of the angle of collision + // Duration of the sound is a function of both base frequency and velocity of impact + float tangentialSpeed = glm::length(velocity) - normalSpeed; + _audio->startCollisionSound( std::min(COLLISION_LOUDNESS * normalSpeed, 1.f), + frequency * (1.f + tangentialSpeed / normalSpeed), + std::min(tangentialSpeed / normalSpeed * NOISE_SCALING, 1.f), + 1.f - DURATION_SCALING * powf(frequency, 0.5f) / normalSpeed, false); + } +} diff --git a/libraries/entities/src/EntityCollisionSystem.h b/libraries/entities/src/EntityCollisionSystem.h new file mode 100644 index 0000000000..fe38209c09 --- /dev/null +++ b/libraries/entities/src/EntityCollisionSystem.h @@ -0,0 +1,73 @@ +// +// EntityCollisionSystem.h +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 9/23/14. +// Copyright 2013-2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_EntityCollisionSystem_h +#define hifi_EntityCollisionSystem_h + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "EntityItem.h" + +class AbstractAudioInterface; +class AvatarData; +class EntityEditPacketSender; +class EntityTree; +class VoxelTree; + +class EntityCollisionSystem : public QObject { +Q_OBJECT +public: + EntityCollisionSystem(EntityEditPacketSender* packetSender = NULL, EntityTree* Entitys = NULL, + VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL, + AvatarHashMap* avatars = NULL); + + void init(EntityEditPacketSender* packetSender, EntityTree* Entitys, VoxelTree* voxels, + AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL); + + ~EntityCollisionSystem(); + + void update(); + + void checkEntity(EntityItem* Entity); + void updateCollisionWithVoxels(EntityItem* Entity); + void updateCollisionWithEntities(EntityItem* Entity); + void updateCollisionWithAvatars(EntityItem* Entity); + void queueEntityPropertiesUpdate(EntityItem* Entity); + void updateCollisionSound(EntityItem* Entity, const glm::vec3 &penetration, float frequency); + +signals: + void EntityCollisionWithVoxel(const EntityItemID& entityItemID, const VoxelDetail& voxel, const CollisionInfo& penetration); + void EntityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const CollisionInfo& penetration); + +private: + static bool updateOperation(OctreeElement* element, void* extraData); + void emitGlobalEntityCollisionWithVoxel(EntityItem* Entity, VoxelDetail* voxelDetails, const CollisionInfo& penetration); + void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const CollisionInfo& penetration); + + EntityEditPacketSender* _packetSender; + EntityTree* _entities; + VoxelTree* _voxels; + AbstractAudioInterface* _audio; + AvatarHashMap* _avatars; + CollisionList _collisions; +}; + +#endif // hifi_EntityCollisionSystem_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 1406239f23..8a0a92c6a5 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -535,6 +535,10 @@ bool EntityItem::isRestingOnSurface() const { void EntityItem::update(const quint64& updateTime) { bool wantDebug = false; + + if (_lastUpdated == 0) { + _lastUpdated = updateTime; + } float timeElapsed = (float)(updateTime - _lastUpdated) / (float)(USECS_PER_SECOND); @@ -578,7 +582,7 @@ void EntityItem::update(const quint64& updateTime) { _lastUpdated = updateTime; if (wantDebug) { - qDebug() << "********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated; + qDebug() << " ********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated; } if (hasAngularVelocity()) { @@ -614,7 +618,7 @@ void EntityItem::update(const quint64& updateTime) { glm::vec3 newPosition = position + (velocity * timeElapsed); if (wantDebug) { - qDebug() << "EntityItem::update()...."; + qDebug() << " EntityItem::update()...."; qDebug() << " timeElapsed:" << timeElapsed; qDebug() << " old AACube:" << getMaximumAACube(); qDebug() << " old position:" << position; @@ -899,3 +903,76 @@ float EntityItem::getRadius() const { return radius; } +void EntityItem::applyHardCollision(const CollisionInfo& collisionInfo) { + // HALTING_* params are determined using expected acceleration of gravity over some timescale. + // This is a HACK for entities that bounce in a 1.0 gravitational field and should eventually be made more universal. + const float HALTING_ENTITY_PERIOD = 0.0167f; // ~1/60th of a second + const float HALTING_ENTITY_SPEED = 9.8 * HALTING_ENTITY_PERIOD / (float)(TREE_SCALE); + + // + // Update the entity in response to a hard collision. Position will be reset exactly + // to outside the colliding surface. Velocity will be modified according to elasticity. + // + // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost) + // if elasticity = 1.0, collision is 100% elastic. + // + glm::vec3 position = getPosition(); + glm::vec3 velocity = getVelocity(); + + const float EPSILON = 0.0f; + glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity; + float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration); + if (velocityDotPenetration < EPSILON) { + // entity is moving into collision surface + // + // TODO: do something smarter here by comparing the mass of the entity vs that of the other thing + // (other's mass could be stored in the Collision Info). The smaller mass should surrender more + // position offset and should slave more to the other's velocity in the static-friction case. + position -= collisionInfo._penetration; + + if (glm::length(relativeVelocity) < HALTING_ENTITY_SPEED) { + // static friction kicks in and entities moves with colliding object + velocity = collisionInfo._addedVelocity; + } else { + glm::vec3 direction = glm::normalize(collisionInfo._penetration); + velocity += glm::dot(relativeVelocity, direction) * (1.0f + collisionInfo._elasticity) * direction; // dynamic reflection + velocity += glm::clamp(collisionInfo._damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction + } + } + + // TODO: how do we handle fixing up the location in the octree cells? + // change the local entity too... + setPosition(position); + setVelocity(velocity); +} + +void EntityItem::collisionWithEntity(EntityItem* other, const glm::vec3& penetration) { + // TODO: how do we want to handle collision related scripts for entities? + /* + // Only run this particle script if there's a script attached directly to the entity. + if (!_script.isEmpty()) { + ScriptEngine engine(_script); + ParticleScriptObject particleScriptable(this); + startParticleScriptContext(engine, particleScriptable); + ParticleScriptObject otherParticleScriptable(other); + particleScriptable.emitCollisionWithParticle(&otherParticleScriptable, penetration); + endParticleScriptContext(engine, particleScriptable); + } + */ +} + +void EntityItem::collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration) { + // TODO: how do we want to handle collision related scripts for entities? + /* + // Only run this entity script if there's a script attached directly to the entity. + if (!_script.isEmpty()) { + ScriptEngine engine(_script); + ParticleScriptObject particleScriptable(this); + startParticleScriptContext(engine, particleScriptable); + particleScriptable.emitCollisionWithVoxel(*voxelDetails, penetration); + endParticleScriptContext(engine, particleScriptable); + } + */ +} + + diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a41d4523f9..7b03c786ed 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -20,6 +20,7 @@ #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState #include +#include #include "EntityItemID.h" #include "EntityItemProperties.h" @@ -227,6 +228,9 @@ public: // TODO: We need to get rid of these users of getRadius()... float getRadius() const; + void applyHardCollision(const CollisionInfo& collisionInfo); + void collisionWithEntity(EntityItem* other, const glm::vec3& penetration); + void collisionWithVoxel(VoxelDetail* voxelDetails, const glm::vec3& penetration); protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f2f2483bb4..facc7e5448 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -24,7 +24,7 @@ EntityItemProperties::EntityItemProperties() : _id(UNKNOWN_ENTITY_ID), _idSet(false), - _lastEdited(0), + _lastEdited(usecTimestampNow()), _created(UNKNOWN_CREATED_TIME), _type(EntityTypes::Unknown), diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index a52a75e071..40e76e1e34 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -90,6 +90,8 @@ public: // editing related features supported by all entities quint64 getLastEdited() const { return _lastEdited; } + float getEditedAgo() const /// Elapsed seconds since this entity was last edited + { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } EntityPropertyFlags getChangedProperties() const; /// used by EntityScriptingInterface to return EntityItemProperties for unknown models @@ -121,14 +123,14 @@ public: float getMass() const { return _mass; } void setMass(float value) { _mass = value; _massChanged = true; } - /// velocity in domain scale units (0.0-1.0) per second + /// velocity in meters (0.0-1.0) per second const glm::vec3& getVelocity() const { return _velocity; } - /// velocity in domain scale units (0.0-1.0) per second + /// velocity in meters (0.0-1.0) per second void setVelocity(const glm::vec3& value) { _velocity = value; _velocityChanged = true; } - /// gravity in domain scale units (0.0-1.0) per second squared + /// gravity in meters (0.0-TREE_SCALE) per second squared const glm::vec3& getGravity() const { return _gravity; } - /// gravity in domain scale units (0.0-1.0) per second squared + /// gravity in meters (0.0-TREE_SCALE) per second squared void setGravity(const glm::vec3& value) { _gravity = value; _gravityChanged = true; } float getDamping() const { return _damping; } @@ -285,4 +287,16 @@ Q_DECLARE_METATYPE(EntityItemProperties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties); + +inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { + debug << "EntityItemProperties[" << "\n" + << " position:" << properties.getPosition() << "in meters" << "\n" + << " velocity:" << properties.getVelocity() << "in meters" << "\n" + << " last edited:" << properties.getLastEdited() << "\n" + << " edited ago:" << properties.getEditedAgo() << "\n" + << "]"; + + return debug; +} + #endif // hifi_EntityItemProperties_h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 1a1412d305..df2e40d96f 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -732,9 +732,6 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { if (!box.expandedContains(args->center, args->radius)) { return false; } - if (!element->isLeaf()) { - return true; // recurse on children - } if (element->hasContent()) { glm::vec3 elementPenetration; if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) { @@ -744,6 +741,9 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { args->found = true; } } + if (!element->isLeaf()) { + return true; // recurse on children + } return false; } diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index e3b568365b..c7a94985cf 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -930,6 +930,9 @@ void Particle::applyHardCollision(const CollisionInfo& collisionInfo) { } void Particle::update(const quint64& now) { + if (_lastUpdated == 0) { + _lastUpdated = now; + } float timeElapsed = (float)(now - _lastUpdated) / (float)(USECS_PER_SECOND); _lastUpdated = now; diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index 45277d6165..4907298a3c 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -5,6 +5,6 @@ setup_hifi_project(Script Network) include_glm() # link in the shared libraries -link_hifi_libraries(animation fbx entities networking octree shared) +link_hifi_libraries(animation avatars fbx entities networking octree shared voxels) link_shared_dependencies()