mirror of
https://github.com/lubosz/overte.git
synced 2025-04-14 06:46:19 +02:00
first cut at getting entities to collide
This commit is contained in:
parent
e485b278ef
commit
f43ba4a68b
11 changed files with 541 additions and 11 deletions
|
@ -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...
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
351
libraries/entities/src/EntityCollisionSystem.cpp
Normal file
351
libraries/entities/src/EntityCollisionSystem.cpp
Normal 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);
|
||||
}
|
||||
}
|
73
libraries/entities/src/EntityCollisionSystem.h
Normal file
73
libraries/entities/src/EntityCollisionSystem.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,7 +24,7 @@ EntityItemProperties::EntityItemProperties() :
|
|||
|
||||
_id(UNKNOWN_ENTITY_ID),
|
||||
_idSet(false),
|
||||
_lastEdited(0),
|
||||
_lastEdited(usecTimestampNow()),
|
||||
_created(UNKNOWN_CREATED_TIME),
|
||||
_type(EntityTypes::Unknown),
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue