History

EntitySimulation takes EntityItem* rather than ID

Andrew Meadows committed 11 years ago

Commit dd3a7b91 parent 0d9855510 files · +162 −292Browse files

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

Unified · Split

libraries/entities/src/EntityCollisionSystem.cpp+0−331
@@ -1,331 +0,0 @@
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 <CollisionInfo.h>
17#include <HeadData.h>
18#include <HandData.h>
19#include <SphereShape.h>
20
21#include "EntityItem.h"
22#include "EntityCollisionSystem.h"
23#include "EntityEditPacketSender.h"
24#include "EntityTree.h"
25#include "EntityTreeElement.h"
26
27const int MAX_COLLISIONS_PER_Entity = 16;
28
29EntityCollisionSystem::EntityCollisionSystem(EntityEditPacketSender* packetSender,
30 EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio,
31 AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_Entity) {
32 init(packetSender, Entities, voxels, audio, avatars);
33}
34
35void EntityCollisionSystem::init(EntityEditPacketSender* packetSender,
36 EntityTree* Entities, VoxelTree* voxels, AbstractAudioInterface* audio,
37 AvatarHashMap* avatars) {
38 _packetSender = packetSender;
39 _entities = Entities;
40 _voxels = voxels;
41 _audio = audio;
42 _avatars = avatars;
43}
44
45EntityCollisionSystem::~EntityCollisionSystem() {
46}
47
48void EntityCollisionSystem::update() {
49 // update all Entities
50 if (_entities->tryLockForRead()) {
51 QList<EntityItem*>& movingEntities = _entities->getMovingEntities();
52 foreach (EntityItem* entity, movingEntities) {
53 checkEntity(entity);
54 }
55 _entities->unlock();
56 }
57}
58
59
60void EntityCollisionSystem::checkEntity(EntityItem* entity) {
61 updateCollisionWithVoxels(entity);
62 updateCollisionWithEntities(entity);
63 updateCollisionWithAvatars(entity);
64}
65
66void EntityCollisionSystem::emitGlobalEntityCollisionWithVoxel(EntityItem* entity,
67 VoxelDetail* voxelDetails, const CollisionInfo& collision) {
68 EntityItemID entityItemID = entity->getEntityItemID();
69 emit entityCollisionWithVoxel(entityItemID, *voxelDetails, collision);
70}
71
72void EntityCollisionSystem::emitGlobalEntityCollisionWithEntity(EntityItem* entityA,
73 EntityItem* entityB, const CollisionInfo& collision) {
74
75 EntityItemID idA = entityA->getEntityItemID();
76 EntityItemID idB = entityB->getEntityItemID();
77 emit entityCollisionWithEntity(idA, idB, collision);
78}
79
80void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) {
81
82 if (entity->getIgnoreForCollisions() || !entity->getCollisionsWillMove()) {
83 return; // bail early if this entity is to be ignored or wont move
84 }
85
86 glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE);
87 float radius = entity->getRadius() * (float)(TREE_SCALE);
88 const float ELASTICITY = 0.4f;
89 const float DAMPING = 0.05f;
90 CollisionInfo collisionInfo;
91 collisionInfo._damping = DAMPING;
92 collisionInfo._elasticity = ELASTICITY;
93 VoxelDetail* voxelDetails = NULL;
94 if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) {
95
96 // findSpherePenetration() only computes the penetration but we also want some other collision info
97 // so we compute it ourselves here. Note that we must multiply scale by TREE_SCALE when feeding
98 // the results to systems outside of this octree reference frame.
99 collisionInfo._contactPoint = (float)TREE_SCALE * (entity->getPosition() + entity->getRadius() * glm::normalize(collisionInfo._penetration));
100 // let the global script run their collision scripts for Entities if they have them
101 emitGlobalEntityCollisionWithVoxel(entity, voxelDetails, collisionInfo);
102
103 // we must scale back down to the octree reference frame before updating the Entity properties
104 collisionInfo._penetration /= (float)(TREE_SCALE);
105 collisionInfo._contactPoint /= (float)(TREE_SCALE);
106
107 applyHardCollision(entity, collisionInfo);
108 delete voxelDetails; // cleanup returned details
109 }
110}
111
112void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
113
114 if (entityA->getIgnoreForCollisions()) {
115 return; // bail early if this entity is to be ignored...
116 }
117
118 // don't collide entities with unknown IDs,
119 if (!entityA->isKnownID()) {
120 return;
121 }
122
123 glm::vec3 penetration;
124 EntityItem* entityB = NULL;
125
126 const int MAX_COLLISIONS_PER_ENTITY = 32;
127 CollisionList collisions(MAX_COLLISIONS_PER_ENTITY);
128 bool shapeCollisionsAccurate = false;
129
130 bool shapeCollisions = _entities->findShapeCollisions(&entityA->getCollisionShapeInMeters(),
131 collisions, Octree::NoLock, &shapeCollisionsAccurate);
132
133
134 if (shapeCollisions) {
135 for(int i = 0; i < collisions.size(); i++) {
136
137 CollisionInfo* collision = collisions[i];
138 penetration = collision->_penetration;
139 entityB = static_cast<EntityItem*>(collision->_extraData);
140
141 // don't collide entities with unknown IDs,
142 if (!entityB->isKnownID()) {
143 continue; // skip this loop pass if the entity has an unknown ID
144 }
145
146 // NOTE: 'penetration' is the depth that 'entityA' overlaps 'entityB'. It points from A into B.
147 glm::vec3 penetrationInTreeUnits = penetration / (float)(TREE_SCALE);
148
149 // Even if the Entities overlap... when the Entities are already moving appart
150 // we don't want to count this as a collision.
151 glm::vec3 relativeVelocity = entityA->getVelocity() - entityB->getVelocity();
152
153 bool fullyEnclosedCollision = glm::length(penetrationInTreeUnits) > entityA->getLargestDimension();
154
155 bool wantToMoveA = entityA->getCollisionsWillMove();
156 bool wantToMoveB = entityB->getCollisionsWillMove();
157 bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f;
158
159 // only do collisions if the entities are moving toward each other and one or the other
160 // of the entities are movable from collisions
161 bool doCollisions = !fullyEnclosedCollision && movingTowardEachOther && (wantToMoveA || wantToMoveB);
162
163 if (doCollisions) {
164
165 quint64 now = usecTimestampNow();
166
167 CollisionInfo collision;
168 collision._penetration = penetration;
169 // for now the contactPoint is the average between the the two paricle centers
170 collision._contactPoint = (0.5f * (float)TREE_SCALE) * (entityA->getPosition() + entityB->getPosition());
171 emitGlobalEntityCollisionWithEntity(entityA, entityB, collision);
172
173 glm::vec3 axis = glm::normalize(penetration);
174 glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis;
175
176 float massA = entityA->getMass();
177 float massB = entityB->getMass();
178 float totalMass = massA + massB;
179 float massRatioA = (2.0f * massB / totalMass);
180 float massRatioB = (2.0f * massA / totalMass);
181
182 // in the event that one of our entities is non-moving, then fix up these ratios
183 if (wantToMoveA && !wantToMoveB) {
184 massRatioA = 2.0f;
185 massRatioB = 0.0f;
186 }
187
188 if (!wantToMoveA && wantToMoveB) {
189 massRatioA = 0.0f;
190 massRatioB = 2.0f;
191 }
192
193 // unless the entity is configured to not be moved by collision, calculate it's new position
194 // and velocity and apply it
195 if (wantToMoveA) {
196 // handle Entity A
197 glm::vec3 newVelocityA = entityA->getVelocity() - axialVelocity * massRatioA;
198 glm::vec3 newPositionA = entityA->getPosition() - 0.5f * penetrationInTreeUnits;
199
200 EntityItemProperties propertiesA = entityA->getProperties();
201 EntityItemID idA(entityA->getID());
202 propertiesA.setVelocity(newVelocityA * (float)TREE_SCALE);
203 propertiesA.setPosition(newPositionA * (float)TREE_SCALE);
204 propertiesA.setLastEdited(now);
205
206 _entities->updateEntity(idA, propertiesA);
207 _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA);
208 }
209
210 // unless the entity is configured to not be moved by collision, calculate it's new position
211 // and velocity and apply it
212 if (wantToMoveB) {
213 glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * massRatioB;
214 glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits;
215
216 EntityItemProperties propertiesB = entityB->getProperties();
217
218 EntityItemID idB(entityB->getID());
219 propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE);
220 propertiesB.setPosition(newPositionB * (float)TREE_SCALE);
221 propertiesB.setLastEdited(now);
222
223 _entities->updateEntity(idB, propertiesB);
224 _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB);
225 }
226 }
227 }
228 }
229}
230
231void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) {
232
233 // Entities that are in hand, don't collide with avatars
234 if (!_avatars) {
235 return;
236 }
237
238 if (entity->getIgnoreForCollisions() || !entity->getCollisionsWillMove()) {
239 return; // bail early if this entity is to be ignored or wont move
240 }
241
242 glm::vec3 center = entity->getPosition() * (float)(TREE_SCALE);
243 float radius = entity->getRadius() * (float)(TREE_SCALE);
244 const float ELASTICITY = 0.9f;
245 const float DAMPING = 0.1f;
246 glm::vec3 penetration;
247
248 _collisions.clear();
249 foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) {
250 AvatarData* avatar = avatarPointer.data();
251
252 float totalRadius = avatar->getBoundingRadius() + radius;
253 glm::vec3 relativePosition = center - avatar->getPosition();
254 if (glm::dot(relativePosition, relativePosition) > (totalRadius * totalRadius)) {
255 continue;
256 }
257
258 if (avatar->findSphereCollisions(center, radius, _collisions)) {
259 int numCollisions = _collisions.size();
260 for (int i = 0; i < numCollisions; ++i) {
261 CollisionInfo* collision = _collisions.getCollision(i);
262 collision->_damping = DAMPING;
263 collision->_elasticity = ELASTICITY;
264
265 collision->_addedVelocity /= (float)(TREE_SCALE);
266 glm::vec3 relativeVelocity = collision->_addedVelocity - entity->getVelocity();
267
268 if (glm::dot(relativeVelocity, collision->_penetration) <= 0.0f) {
269 // only collide when Entity and collision point are moving toward each other
270 // (doing this prevents some "collision snagging" when Entity penetrates the object)
271 collision->_penetration /= (float)(TREE_SCALE);
272 applyHardCollision(entity, *collision);
273 }
274 }
275 }
276 }
277}
278
279void EntityCollisionSystem::applyHardCollision(EntityItem* entity, const CollisionInfo& collisionInfo) {
280
281 // don't collide entities with unknown IDs,
282 if (!entity->isKnownID()) {
283 return;
284 }
285
286 // HALTING_* params are determined using expected acceleration of gravity over some timescale.
287 // This is a HACK for entities that bounce in a 1.0 gravitational field and should eventually be made more universal.
288 const float HALTING_ENTITY_PERIOD = 0.0167f; // ~1/60th of a second
289 const float HALTING_ENTITY_SPEED = 9.8 * HALTING_ENTITY_PERIOD / (float)(TREE_SCALE);
290
291 //
292 // Update the entity in response to a hard collision. Position will be reset exactly
293 // to outside the colliding surface. Velocity will be modified according to elasticity.
294 //
295 // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost)
296 // if elasticity = 1.0, collision is 100% elastic.
297 //
298 glm::vec3 position = entity->getPosition();
299 glm::vec3 velocity = entity->getVelocity();
300
301 const float EPSILON = 0.0f;
302 glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity;
303 float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration);
304 if (velocityDotPenetration < EPSILON) {
305 // entity is moving into collision surface
306 //
307 // TODO: do something smarter here by comparing the mass of the entity vs that of the other thing
308 // (other's mass could be stored in the Collision Info). The smaller mass should surrender more
309 // position offset and should slave more to the other's velocity in the static-friction case.
310 position -= collisionInfo._penetration;
311
312 if (glm::length(relativeVelocity) < HALTING_ENTITY_SPEED) {
313 // static friction kicks in and entities moves with colliding object
314 velocity = collisionInfo._addedVelocity;
315 } else {
316 glm::vec3 direction = glm::normalize(collisionInfo._penetration);
317 velocity += glm::dot(relativeVelocity, direction) * (1.0f + collisionInfo._elasticity) * direction; // dynamic reflection
318 velocity += glm::clamp(collisionInfo._damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction
319 }
320 }
321
322 EntityItemProperties properties = entity->getProperties();
323 EntityItemID entityItemID(entity->getID());
324
325 properties.setPosition(position * (float)TREE_SCALE);
326 properties.setVelocity(velocity * (float)TREE_SCALE);
327 properties.setLastEdited(usecTimestampNow());
328
329 _entities->updateEntity(entityItemID, properties);
330 _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties);
331}

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