first cut at getting entities to collide

This commit is contained in:
ZappoMan 2014-09-24 15:28:04 -07:00
parent e485b278ef
commit f43ba4a68b
11 changed files with 541 additions and 11 deletions

View file

@ -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...
}
}
{

View file

@ -31,6 +31,7 @@
#include <QSystemTrayIcon>
#include <EntityEditPacketSender.h>
#include <EntityCollisionSystem.h>
#include <NetworkPacket.h>
#include <NodeList.h>
#include <PacketHeaders.h>
@ -482,6 +483,7 @@ private:
ParticleCollisionSystem _particleCollisionSystem;
EntityTreeRenderer _entities;
EntityCollisionSystem _entityCollisionSystem;
EntityTreeRenderer _entityClipboardRenderer;
EntityTree _entityClipboard;

View file

@ -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 <algorithm>
#include <AbstractAudioInterface.h>
#include <VoxelTree.h>
#include <AvatarData.h>
#include <HeadData.h>
#include <HandData.h>
#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<EntityCollisionSystem*>(extraData);
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
// iterate the Entities...
QList<EntityItem*>& 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);
}
}

View file

@ -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 <glm/glm.hpp>
#include <stdint.h>
#include <QtScript/QScriptEngine>
#include <QtCore/QObject>
#include <AvatarHashMap.h>
#include <CollisionInfo.h>
#include <SharedUtil.h>
#include <OctreePacketData.h>
#include <VoxelDetail.h>
#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

View file

@ -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);
}
*/
}

View file

@ -20,6 +20,7 @@
#include <Octree.h> // for EncodeBitstreamParams class
#include <OctreeElement.h> // for OctreeElement::AppendState
#include <OctreePacketData.h>
#include <VoxelDetail.h>
#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

View file

@ -24,7 +24,7 @@ EntityItemProperties::EntityItemProperties() :
_id(UNKNOWN_ENTITY_ID),
_idSet(false),
_lastEdited(0),
_lastEdited(usecTimestampNow()),
_created(UNKNOWN_CREATED_TIME),
_type(EntityTypes::Unknown),

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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()