History

first cut at getting entities to collide

ZappoMan committed 12 years ago

Commit f43ba4a1 parent e485b2711 files · +541 −11Browse files

Showing the changes to libraries/entities/src/EntityCollisionSystem.cpp (1 of 11 files). Show all

Unified · Split

libraries/entities/src/EntityCollisionSystem.cpp+351−0
@@ -0,0 +1,351 @@
1//
2// EntityCollisionSystem.cpp
3// libraries/entities/src
4//
5// Created by Brad Hefta-Gaub on 9/23/14.
6// Copyright 2013-2014 High Fidelity, Inc.
7//
8// Distributed under the Apache License, Version 2.0.
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//
11
12#include <algorithm>
13#include <AbstractAudioInterface.h>
14#include <VoxelTree.h>
15#include <AvatarData.h>
16#include <HeadData.h>
17#include <HandData.h>
18
19#include "EntityItem.h"
20#include "EntityCollisionSystem.h"
21#include "EntityEditPacketSender.h"
22#include "EntityTree.h"
23#include "EntityTreeElement.h"
24
25const int MAX_COLLISIONS_PER_Entity = 16;
26
27EntityCollisionSystem::EntityCollisionSystem(EntityEditPacketSender* packetSender,
28 EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio,
29 AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_Entity) {
30 init(packetSender, Entities, voxels, audio, avatars);
31}
32
33void EntityCollisionSystem::init(EntityEditPacketSender* packetSender,
34 EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio,
35 AvatarHashMap* avatars) {
36 _packetSender = packetSender;
37 _entities = Entities;
38 _voxels = voxels;
39 _audio = audio;
40 _avatars = avatars;
41}
42
43EntityCollisionSystem::~EntityCollisionSystem() {
44}
45
46bool EntityCollisionSystem::updateOperation(OctreeElement* element, void* extraData) {
47 EntityCollisionSystem* system = static_cast<EntityCollisionSystem*>(extraData);
48 EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
49
50 // iterate the Entities...
51 QList<EntityItem*>& entities = entityTreeElement->getEntities();
52 uint16_t numberOfEntities = entities.size();
53 for (uint16_t i = 0; i < numberOfEntities; i++) {
54 EntityItem* entity = entities[i];
55 system->checkEntity(entity);
56 }
57
58 return true;
59}
60
61
62void EntityCollisionSystem::update() {
63 // update all Entities
64 if (_entities->tryLockForRead()) {
65 _entities->recurseTreeWithOperation(updateOperation, this);
66 _entities->unlock();
67 }
68}
69
70
71void EntityCollisionSystem::checkEntity(EntityItem* entity) {
72 updateCollisionWithVoxels(entity);
73 updateCollisionWithEntities(entity);
74 updateCollisionWithAvatars(entity);
75}
76
77void EntityCollisionSystem::emitGlobalEntityCollisionWithVoxel(EntityItem* entity,
78 VoxelDetail* voxelDetails, const CollisionInfo& collision) {
79 EntityItemID entityItemID = entity->getEntityItemID();
80 emit EntityCollisionWithVoxel(entityItemID, *voxelDetails, collision);
81}
82
83void EntityCollisionSystem::emitGlobalEntityCollisionWithEntity(EntityItem* entityA,
84 EntityItem* entityB, const CollisionInfo& collision) {
85
86 EntityItemID idA = entityA->getEntityItemID();
87 EntityItemID idB = entityB->getEntityItemID();
88 emit EntityCollisionWithEntity(idA, idB, collision);
89}
90
91void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) {
92 glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE);
93 float radius = entity->getRadius() * (float)(TREE_SCALE);
94 const float ELASTICITY = 0.4f;
95 const float DAMPING = 0.05f;
96 const float COLLISION_FREQUENCY = 0.5f;
97 CollisionInfo collisionInfo;
98 collisionInfo._damping = DAMPING;
99 collisionInfo._elasticity = ELASTICITY;
100 VoxelDetail* voxelDetails = NULL;
101 if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) {
102
103 // let the Entities run their collision scripts if they have them
104 //entity->collisionWithVoxel(voxelDetails, collisionInfo._penetration);
105
106 // findSpherePenetration() only computes the penetration but we also want some other collision info
107 // so we compute it ourselves here. Note that we must multiply scale by TREE_SCALE when feeding
108 // the results to systems outside of this octree reference frame.
109 updateCollisionSound(entity, collisionInfo._penetration, COLLISION_FREQUENCY);
110 collisionInfo._contactPoint = (float)TREE_SCALE * (entity->getPosition() + entity->getRadius() * glm::normalize(collisionInfo._penetration));
111 // let the global script run their collision scripts for Entities if they have them
112 emitGlobalEntityCollisionWithVoxel(entity, voxelDetails, collisionInfo);
113
114 // we must scale back down to the octree reference frame before updating the Entity properties
115 collisionInfo._penetration /= (float)(TREE_SCALE);
116 collisionInfo._contactPoint /= (float)(TREE_SCALE);
117 entity->applyHardCollision(collisionInfo);
118 queueEntityPropertiesUpdate(entity);
119
120 delete voxelDetails; // cleanup returned details
121 }
122}
123
124void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
125 glm::vec3 center = entityA->getPosition() * (float)(TREE_SCALE);
126 float radius = entityA->getRadius() * (float)(TREE_SCALE);
127 //const float ELASTICITY = 0.4f;
128 //const float DAMPING = 0.0f;
129 const float COLLISION_FREQUENCY = 0.5f;
130 glm::vec3 penetration;
131 EntityItem* entityB;
132 if (_entities->findSpherePenetration(center, radius, penetration, (void**)&entityB, Octree::NoLock)) {
133 // NOTE: 'penetration' is the depth that 'entityA' overlaps 'entityB'. It points from A into B.
134
135qDebug() << "penetration:" << penetration << "in meters";
136
137 glm::vec3 penetrationInTreeUnits = penetration / (float)(TREE_SCALE);
138
139qDebug() << "penetrationInTreeUnits:" << penetrationInTreeUnits;
140
141 // Even if the Entities overlap... when the Entities are already moving appart
142 // we don't want to count this as a collision.
143 glm::vec3 relativeVelocity = entityA->getVelocity() - entityB->getVelocity();
144
145qDebug() << "A old velocity:" << entityA->getVelocity() * (float)TREE_SCALE << "in meters";
146qDebug() << "B old velocity:" << entityB->getVelocity() * (float)TREE_SCALE << "in meters";
147qDebug() << "relativeVelocity:" << relativeVelocity * (float)TREE_SCALE << "in meters";
148
149 bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f;
150 bool doCollisions = true;
151
152 if (doCollisions) {
153 //entityA->collisionWithEntity(entityB, penetration);
154 //entityB->collisionWithEntity(entityA, penetration * -1.0f); // the penetration is reversed
155
156 CollisionInfo collision;
157 collision._penetration = penetration;
158 // for now the contactPoint is the average between the the two paricle centers
159 collision._contactPoint = (0.5f * (float)TREE_SCALE) * (entityA->getPosition() + entityB->getPosition());
160 //emitGlobalEntityCollisionWithEntity(entityA, entityB, collision);
161
162 glm::vec3 axis = glm::normalize(penetration);
163 glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis;
164
165 float massA = entityA->getMass();
166 float massB = entityB->getMass();
167 float totalMass = massA + massB;
168qDebug() << "massA:" << massA;
169qDebug() << "massB:" << massB;
170qDebug() << "totalMass:" << totalMass;
171
172 // handle Entity A
173
174qDebug() << "OLD - VALUES - entityA ----";
175qDebug() << "entityA->getVelocity():" << entityA->getVelocity() << "no scaling";
176qDebug() << "entityA->getVelocity():" << entityA->getVelocity() * (float)TREE_SCALE << "in meters";
177qDebug() << "entityA->getPosition():" << entityA->getPosition() * (float)TREE_SCALE << "in meters";
178
179
180 glm::vec3 newVelocity = entityA->getVelocity() - axialVelocity * (2.0f * massB / totalMass);
181 glm::vec3 newPosition = entityA->getPosition() - 0.5f * penetrationInTreeUnits;
182
183qDebug() << "A new Velocity:" << newVelocity * (float)TREE_SCALE << "in meters";
184qDebug() << "A new Position:" << newPosition * (float)TREE_SCALE << "in meters";
185
186 EntityItemProperties propertiesA = entityA->getProperties();
187
188qDebug() << "propertiesA = entityA->getProperties() --- BEFORE SETTING ----";
189qDebug() << " propertiesA.getPosition():" << propertiesA.getPosition() << "in meters";
190qDebug() << " propertiesA.getVelocity():" << propertiesA.getVelocity() << "in meters";
191qDebug() << " propertiesA.getMaximumAACubeInTreeUnits():" << propertiesA.getMaximumAACubeInTreeUnits();
192
193 EntityItemID idA(entityA->getID());
194 propertiesA.setVelocity(newVelocity * (float)TREE_SCALE);
195 propertiesA.setPosition(newPosition * (float)TREE_SCALE);
196
197qDebug() << "updateEntity(idA, propertiesA)....";
198qDebug() << " idA:" << idA;
199qDebug() << " propertiesA.getPosition():" << propertiesA.getPosition() << "in meters";
200qDebug() << " propertiesA.getVelocity():" << propertiesA.getVelocity() << "in meters";
201qDebug() << " propertiesA.getMaximumAACubeInTreeUnits():" << propertiesA.getMaximumAACubeInTreeUnits();
202
203 _entities->updateEntity(idA, propertiesA);
204
205qDebug() << "AFTER updateEntity()....";
206qDebug() << " entityA->getVelocity():" << entityA->getVelocity() << "no scaling";
207qDebug() << " entityA->getPosition():" << entityA->getPosition() * (float)TREE_SCALE << "in meters";
208
209
210 _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA);
211 //queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
212
213qDebug() << "OLD - VALUES - entityB ----";
214qDebug() << " entityB->getVelocity():" << entityB->getVelocity() << "no scaling";
215qDebug() << " entityB->getPosition():" << entityB->getPosition() * (float)TREE_SCALE << "in meters";
216
217 glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * (2.0f * massA / totalMass);
218 glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits;
219
220qDebug() << "B new Velocity:" << newVelocityB << "no scaling";
221qDebug() << "B new Position:" << newPositionB * (float)TREE_SCALE << "in meters";
222
223 EntityItemProperties propertiesB = entityB->getProperties();
224
225qDebug() << "propertiesB = entityB->getProperties()";
226qDebug() << " propertiesB.getPosition():" << propertiesB.getPosition() << "not scaled, should be meters";
227qDebug() << " propertiesB.getVelocity():" << propertiesB.getVelocity() << "not scaled, should be ???";
228qDebug() << " propertiesB.getMaximumAACubeInTreeUnits():" << propertiesB.getMaximumAACubeInTreeUnits();
229
230 EntityItemID idB(entityB->getID());
231 propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE);
232 propertiesB.setPosition(newPositionB * (float)TREE_SCALE);
233
234qDebug() << "updateEntity(idB, propertiesB)....";
235qDebug() << " idB:" << idB;
236qDebug() << " propertiesB.getPosition():" << propertiesB.getPosition();
237qDebug() << " propertiesB.getVelocity():" << propertiesB.getVelocity();
238qDebug() << " propertiesB.getMaximumAACubeInTreeUnits():" << propertiesB.getMaximumAACubeInTreeUnits();
239
240 _entities->updateEntity(idB, propertiesB);
241
242qDebug() << "AFTER updateEntity()....";
243qDebug() << " entityB->getVelocity():" << entityB->getVelocity() << "no scaling";
244qDebug() << " entityB->getPosition():" << entityB->getPosition() * (float)TREE_SCALE << "in meters";
245
246 _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB);
247 //_packetSender->releaseQueuedMessages();
248
249 //updateCollisionSound(entityA, penetration, COLLISION_FREQUENCY);
250 }
251 }
252}
253
254void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) {
255
256#if 0
257 // Entities that are in hand, don't collide with avatars
258 if (!_avatars) {
259 return;
260 }
261
262 glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE);
263 float radius = entity->getRadius() * (float)(TREE_SCALE);
264 const float ELASTICITY = 0.9f;
265 const float DAMPING = 0.1f;
266 const float COLLISION_FREQUENCY = 0.5f;
267 glm::vec3 penetration;
268
269 _collisions.clear();
270 foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) {
271 AvatarData* avatar = avatarPointer.data();
272
273 float totalRadius = avatar->getBoundingRadius() + radius;
274 glm::vec3 relativePosition = center - avatar->getPosition();
275 if (glm::dot(relativePosition, relativePosition) > (totalRadius * totalRadius)) {
276 continue;
277 }
278
279 if (avatar->findSphereCollisions(center, radius, _collisions)) {
280 int numCollisions = _collisions.size();
281 for (int i = 0; i < numCollisions; ++i) {
282 CollisionInfo* collision = _collisions.getCollision(i);
283 collision->_damping = DAMPING;
284 collision->_elasticity = ELASTICITY;
285
286 collision->_addedVelocity /= (float)(TREE_SCALE);
287 glm::vec3 relativeVelocity = collision->_addedVelocity - entity->getVelocity();
288
289 if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) {
290 // only collide when Entity and collision point are moving toward each other
291 // (doing this prevents some "collision snagging" when Entity penetrates the object)
292 updateCollisionSound(entity, collision->_penetration, COLLISION_FREQUENCY);
293 collision->_penetration /= (float)(TREE_SCALE);
294 entity->applyHardCollision(*collision);
295 queueEntityPropertiesUpdate(entity);
296 }
297 }
298 }
299 }
300
301#endif
302}
303
304void EntityCollisionSystem::queueEntityPropertiesUpdate(EntityItem* entity) {
305 // queue the result for sending to the Entity server
306 EntityItemProperties properties = entity->getProperties();
307 EntityItemID entityItemID(entity->getID());
308
309 properties.setPosition(entity->getPosition() * (float)TREE_SCALE);
310 properties.setVelocity(entity->getVelocity() * (float)TREE_SCALE);
311 _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties);
312}
313
314
315void EntityCollisionSystem::updateCollisionSound(EntityItem* Entity, const glm::vec3 &penetration, float frequency) {
316
317 // consider whether to have the collision make a sound
318 const float AUDIBLE_COLLISION_THRESHOLD = 0.3f;
319 const float COLLISION_LOUDNESS = 1.f;
320 const float DURATION_SCALING = 0.004f;
321 const float NOISE_SCALING = 0.1f;
322 glm::vec3 velocity = Entity->getVelocity() * (float)(TREE_SCALE);
323
324 /*
325 // how do we want to handle this??
326 //
327 glm::vec3 gravity = Entity->getGravity() * (float)(TREE_SCALE);
328
329 if (glm::length(gravity) > EPSILON) {
330 // If gravity is on, remove the effect of gravity on velocity for this
331 // frame, so that we are not constantly colliding with the surface
332 velocity -= _scale * glm::length(gravity) * GRAVITY_EARTH * deltaTime * glm::normalize(gravity);
333 }
334 */
335 float normalSpeed = glm::dot(velocity, glm::normalize(penetration));
336 // NOTE: it is possible for normalSpeed to be NaN at this point
337 // (sometimes the average penetration of a bunch of voxels is a zero length vector which cannot be normalized)
338 // however the check below will fail (NaN comparisons always fail) and everything will be fine.
339
340 if (normalSpeed > AUDIBLE_COLLISION_THRESHOLD) {
341 // Volume is proportional to collision velocity
342 // Base frequency is modified upward by the angle of the collision
343 // Noise is a function of the angle of collision
344 // Duration of the sound is a function of both base frequency and velocity of impact
345 float tangentialSpeed = glm::length(velocity) - normalSpeed;
346 _audio->startCollisionSound( std::min(COLLISION_LOUDNESS * normalSpeed, 1.f),
347 frequency * (1.f + tangentialSpeed / normalSpeed),
348 std::min(tangentialSpeed / normalSpeed * NOISE_SCALING, 1.f),
349 1.f - DURATION_SCALING * powf(frequency, 0.5f) / normalSpeed, false);
350 }
351}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t