From 549e3fac5f528573f62b22bd774cfec6e8b45255 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Jan 2015 10:45:58 -0800 Subject: [PATCH 01/59] store backpointer to MotionState in btRigidBody --- libraries/physics/src/ObjectMotionState.cpp | 13 +++++++++++++ libraries/physics/src/ObjectMotionState.h | 7 +++++-- libraries/physics/src/PhysicsEngine.cpp | 12 ++++++------ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index f4997b679c..cab36b8370 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -170,3 +170,16 @@ void ObjectMotionState::removeKinematicController() { _kinematicController = NULL; } } + +void ObjectMotionState::setRigidBody(btRigidBody* body) { + // give the body a (void*) back-pointer to this ObjectMotionState + if (_body != body) { + if (_body) { + _body->setUserPointer(NULL); + } + _body = body; + if (_body) { + _body->setUserPointer(this); + } + } +} diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 4d454554cb..0620d030b2 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -88,9 +88,13 @@ public: virtual void addKinematicController() = 0; virtual void removeKinematicController(); + btRigidBody* getRigidBody() const { return _body; } + friend class PhysicsEngine; protected: - // TODO: move these materials properties to EntityItem + void setRigidBody(btRigidBody* body); + + // TODO: move these materials properties outside of ObjectMotionState float _friction; float _restitution; float _linearDamping; @@ -98,7 +102,6 @@ protected: MotionType _motionType; - // _body has NO setters -- it is only changed by PhysicsEngine btRigidBody* _body; bool _sentMoving; // true if last update was moving diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 90fd6c65cd..ca64184bd7 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -132,7 +132,7 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { ObjectMotionState* motionState = *stateItr; uint32_t flags = motionState->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; - btRigidBody* body = motionState->_body; + btRigidBody* body = motionState->getRigidBody(); if (body) { if (flags & HARD_DIRTY_PHYSICS_FLAGS) { // a HARD update requires the body be pulled out of physics engine, changed, then reinserted @@ -258,7 +258,7 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) { body = new btRigidBody(mass, motionState, shape, inertia); body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->updateInertiaTensor(); - motionState->_body = body; + motionState->setRigidBody(body); motionState->addKinematicController(); const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec @@ -270,7 +270,7 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) { shape->calculateLocalInertia(mass, inertia); body = new btRigidBody(mass, motionState, shape, inertia); body->updateInertiaTensor(); - motionState->_body = body; + motionState->setRigidBody(body); motionState->updateObjectVelocities(); // NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds. // (the 2 seconds is determined by: static btRigidBody::gDeactivationTime @@ -284,7 +284,7 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) { body = new btRigidBody(mass, motionState, shape, inertia); body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); body->updateInertiaTensor(); - motionState->_body = body; + motionState->setRigidBody(body); break; } } @@ -301,7 +301,7 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) { bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { assert(motionState); - btRigidBody* body = motionState->_body; + btRigidBody* body = motionState->getRigidBody(); if (body) { const btCollisionShape* shape = body->getCollisionShape(); ShapeInfo shapeInfo; @@ -309,7 +309,7 @@ bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { _dynamicsWorld->removeRigidBody(body); _shapeManager.releaseShape(shapeInfo); delete body; - motionState->_body = NULL; + motionState->setRigidBody(NULL); motionState->removeKinematicController(); return true; } From 0154c613d0af2f8c806e70026b7c21dc41788943 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Jan 2015 11:02:48 -0800 Subject: [PATCH 02/59] change FrameCount to NumSubsteps for more accuracy --- libraries/physics/src/KinematicController.cpp | 4 ++-- libraries/physics/src/KinematicController.h | 2 +- libraries/physics/src/PhysicsEngine.cpp | 24 ++++++++++++------- libraries/physics/src/PhysicsEngine.h | 4 +++- .../src/SimpleEntityKinematicController.cpp | 6 ++--- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/libraries/physics/src/KinematicController.cpp b/libraries/physics/src/KinematicController.cpp index abcf669694..354b285bc1 100644 --- a/libraries/physics/src/KinematicController.cpp +++ b/libraries/physics/src/KinematicController.cpp @@ -13,10 +13,10 @@ #include "PhysicsEngine.h" KinematicController::KinematicController() { - _lastFrame = PhysicsEngine::getFrameCount(); + _lastSubstep = PhysicsEngine::getNumSubsteps(); } void KinematicController::start() { _enabled = true; - _lastFrame = PhysicsEngine::getFrameCount(); + _lastSubstep = PhysicsEngine::getNumSubsteps(); } diff --git a/libraries/physics/src/KinematicController.h b/libraries/physics/src/KinematicController.h index 382d4cbfd9..60b8548607 100644 --- a/libraries/physics/src/KinematicController.h +++ b/libraries/physics/src/KinematicController.h @@ -30,7 +30,7 @@ public: protected: bool _enabled = false; - uint32_t _lastFrame; + uint32_t _lastSubstep; }; #endif // hifi_KinematicController_h diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index ca64184bd7..af4a49aefe 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -13,11 +13,11 @@ #include "ShapeInfoUtil.h" #include "ThreadSafeDynamicsWorld.h" -static uint32_t _frameCount; +static uint32_t _numSubsteps; // static -uint32_t PhysicsEngine::getFrameCount() { - return _frameCount; +uint32_t PhysicsEngine::getNumSubsteps() { + return _numSubsteps; } PhysicsEngine::PhysicsEngine(const glm::vec3& offset) @@ -47,8 +47,8 @@ void PhysicsEngine::updateEntitiesInternal(const quint64& now) { ObjectMotionState* state = *stateItr; if (state->doesNotNeedToSendUpdate()) { stateItr = _outgoingPackets.erase(stateItr); - } else if (state->shouldSendUpdate(_frameCount)) { - state->sendUpdate(_entityPacketSender, _frameCount); + } else if (state->shouldSendUpdate(_numSubsteps)) { + state->sendUpdate(_entityPacketSender, _numSubsteps); ++stateItr; } else { ++stateItr; @@ -141,7 +141,7 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { } else if (flags) { // an EASY update does NOT require that the body be pulled out of physics engine // hence the MotionState has all the knowledge and authority to perform the update. - motionState->updateObjectEasy(flags, _frameCount); + motionState->updateObjectEasy(flags, _numSubsteps); } } @@ -216,8 +216,8 @@ void PhysicsEngine::stepSimulation() { float timeStep = btMin(dt, MAX_TIMESTEP); // This is step (2). - int numSubSteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); - _frameCount += (uint32_t)numSubSteps; + int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); + _numSubsteps += (uint32_t)numSubsteps; unlock(); // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. @@ -233,6 +233,12 @@ void PhysicsEngine::stepSimulation() { _dynamicsWorld->synchronizeMotionStates(); unlock(); _entityTree->unlock(); + + handleCollisionEvents(); +} + +void PhysicsEngine::handleCollisionEvents() { + } // Bullet collision flags are as follows: @@ -352,7 +358,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio } bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS; if (easyUpdate) { - motionState->updateObjectEasy(flags, _frameCount); + motionState->updateObjectEasy(flags, _numSubsteps); } // update the motion parameters diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index f37f12ea8d..7304f598e2 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -33,7 +33,7 @@ class ObjectMotionState; class PhysicsEngine : public EntitySimulation { public: - static uint32_t getFrameCount(); + static uint32_t getNumSubsteps(); PhysicsEngine(const glm::vec3& offset); @@ -51,6 +51,8 @@ public: void stepSimulation(); + void handleCollisionEvents(); + /// \param offset position of simulation origin in domain-frame void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; } diff --git a/libraries/physics/src/SimpleEntityKinematicController.cpp b/libraries/physics/src/SimpleEntityKinematicController.cpp index 478ec1d12f..e834d4e91b 100644 --- a/libraries/physics/src/SimpleEntityKinematicController.cpp +++ b/libraries/physics/src/SimpleEntityKinematicController.cpp @@ -13,9 +13,9 @@ #include "SimpleEntityKinematicController.h" void SimpleEntityKinematicController:: stepForward() { - uint32_t frame = PhysicsEngine::getFrameCount(); - float dt = (frame - _lastFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP; + uint32_t substep = PhysicsEngine::getNumSubsteps(); + float dt = (substep - _lastSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; _entity->simulateSimpleKinematicMotion(dt); - _lastFrame = frame; + _lastSubstep = substep; } From 5da38834e2e762f387ecc23cd1c48bdd92e12020 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Jan 2015 11:16:40 -0800 Subject: [PATCH 03/59] introduce notion of NumSteps vs NumSubsteps --- libraries/physics/src/PhysicsEngine.cpp | 12 +++--------- libraries/physics/src/PhysicsEngine.h | 23 +++++++++++------------ 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index af4a49aefe..870fdc6a9d 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -21,13 +21,7 @@ uint32_t PhysicsEngine::getNumSubsteps() { } PhysicsEngine::PhysicsEngine(const glm::vec3& offset) - : _collisionConfig(NULL), - _collisionDispatcher(NULL), - _broadphaseFilter(NULL), - _constraintSolver(NULL), - _dynamicsWorld(NULL), - _originOffset(offset), - _entityPacketSender(NULL) { + : _originOffset(offset) { } PhysicsEngine::~PhysicsEngine() { @@ -234,10 +228,10 @@ void PhysicsEngine::stepSimulation() { unlock(); _entityTree->unlock(); - handleCollisionEvents(); + computeCollisionEvents(); } -void PhysicsEngine::handleCollisionEvents() { +void PhysicsEngine::computeCollisionEvents() { } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 7304f598e2..1f4efc6b06 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -33,8 +33,10 @@ class ObjectMotionState; class PhysicsEngine : public EntitySimulation { public: + // TODO: find a good way to make this a non-static method static uint32_t getNumSubsteps(); + PhysicsEngine() = delete; // prevent compiler from creating default ctor PhysicsEngine(const glm::vec3& offset); ~PhysicsEngine(); @@ -51,7 +53,7 @@ public: void stepSimulation(); - void handleCollisionEvents(); + void computeCollisionEvents(); /// \param offset position of simulation origin in domain-frame void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; } @@ -70,22 +72,18 @@ public: /// process queue of changed from external sources void relayIncomingChangesToSimulation(); - /// \return duration of fixed simulation substep - float getFixedSubStep() const; - -protected: +private: void updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); btClock _clock; - btDefaultCollisionConfiguration* _collisionConfig; - btCollisionDispatcher* _collisionDispatcher; - btBroadphaseInterface* _broadphaseFilter; - btSequentialImpulseConstraintSolver* _constraintSolver; - ThreadSafeDynamicsWorld* _dynamicsWorld; + btDefaultCollisionConfiguration* _collisionConfig = NULL; + btCollisionDispatcher* _collisionDispatcher = NULL; + btBroadphaseInterface* _broadphaseFilter = NULL; + btSequentialImpulseConstraintSolver* _constraintSolver = NULL; + ThreadSafeDynamicsWorld* _dynamicsWorld = NULL; ShapeManager _shapeManager; -private: glm::vec3 _originOffset; // EntitySimulation stuff @@ -93,7 +91,8 @@ private: QSet _incomingChanges; // entities with pending physics changes by script or packet QSet _outgoingPackets; // MotionStates with pending changes that need to be sent over wire - EntityEditPacketSender* _entityPacketSender; + EntityEditPacketSender* _entityPacketSender = NULL; + uint32_t _numSteps = 0; // do not confuse with static _numSubsteps }; #endif // hifi_PhysicsEngine_h From 790d07d3461732094c87a928f3bc5eb0df7505cd Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Jan 2015 14:18:10 -0800 Subject: [PATCH 04/59] track known contacts and generate collision events --- libraries/physics/src/ObjectMotionState.cpp | 8 ++++ libraries/physics/src/ObjectMotionState.h | 3 ++ libraries/physics/src/PhysicsEngine.cpp | 53 ++++++++++++++++++++- libraries/physics/src/PhysicsEngine.h | 17 ++++++- 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index cab36b8370..929ddab3e0 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -171,6 +171,14 @@ void ObjectMotionState::removeKinematicController() { } } +void ObjectMotionState::handleContactEvent(ContactEventType type, ObjectMotionState* otherState) { + if (type == CONTACT_TYPE_START) { + std::cout << "adebug start " << (void*)(this) << " vs " << (void*)(otherState) << std::endl; // adebug + } else if (type == CONTACT_TYPE_END) { + std::cout << "adebug end " << (void*)(this) << " vs " << (void*)(otherState) << std::endl; // adebug + } +} + void ObjectMotionState::setRigidBody(btRigidBody* body) { // give the body a (void*) back-pointer to this ObjectMotionState if (_body != body) { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 0620d030b2..2eda618d93 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -17,6 +17,7 @@ #include +#include "ContactInfo.h" #include "ShapeInfo.h" enum MotionType { @@ -88,6 +89,8 @@ public: virtual void addKinematicController() = 0; virtual void removeKinematicController(); + virtual void handleContactEvent(ContactEventType type, ObjectMotionState* otherState); + btRigidBody* getRigidBody() const { return _body; } friend class PhysicsEngine; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 870fdc6a9d..5a2881da71 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -154,6 +154,20 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { _incomingChanges.clear(); } +void PhysicsEngine::removeContacts(ObjectMotionState* motionState) { + // trigger events for new/existing/old contacts + ContactMap::iterator contactItr = _contactMap.begin(); + while (contactItr != _contactMap.end()) { + if (contactItr->first._a == motionState || contactItr->first._b == motionState) { + ContactMap::iterator iterToDelete = contactItr; + ++contactItr; + _contactMap.erase(iterToDelete); + } else { + ++contactItr; + } + } +} + // virtual void PhysicsEngine::init(EntityEditPacketSender* packetSender) { // _entityTree should be set prior to the init() call @@ -232,7 +246,42 @@ void PhysicsEngine::stepSimulation() { } void PhysicsEngine::computeCollisionEvents() { - + // update all contacts + int numManifolds = _collisionDispatcher->getNumManifolds(); + for (int i = 0; i < numManifolds; ++i) { + btPersistentManifold* contactManifold = _collisionDispatcher->getManifoldByIndexInternal(i); + if (contactManifold->getNumContacts() > 0) { + const btCollisionObject* objectA = static_cast(contactManifold->getBody0()); + const btCollisionObject* objectB = static_cast(contactManifold->getBody1()); + + void* a = objectA->getUserPointer(); + void* b = objectB->getUserPointer(); + if (a || b ) { + _contactMap[ContactKey(a, b)].update(_numSubsteps); + } + } + } + + // scan known contacts and trigger events + ContactMap::iterator contactItr = _contactMap.begin(); + while (contactItr != _contactMap.end()) { + ContactEventType type = contactItr->second.computeType(_numSubsteps); + ObjectMotionState* A = static_cast(contactItr->first._a); + ObjectMotionState* B = static_cast(contactItr->first._b); + if (A) { + A->handleContactEvent(type, B); + } + if (B) { + B->handleContactEvent(type, A); + } + if (type == CONTACT_TYPE_END) { + ContactMap::iterator iterToDelete = contactItr; + ++contactItr; + _contactMap.erase(iterToDelete); + } else { + ++contactItr; + } + } } // Bullet collision flags are as follows: @@ -311,6 +360,8 @@ bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { delete body; motionState->setRigidBody(NULL); motionState->removeKinematicController(); + + removeContacts(motionState); return true; } return false; diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 1f4efc6b06..9db9f5a33a 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -23,6 +23,7 @@ const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 60.0f; #include #include "BulletUtil.h" +#include "ContactInfo.h" #include "EntityMotionState.h" #include "ShapeManager.h" #include "ThreadSafeDynamicsWorld.h" @@ -31,6 +32,18 @@ const float HALF_SIMULATION_EXTENT = 512.0f; // meters class ObjectMotionState; +// simple class for keeping track of contacts +class ContactKey { +public: + ContactKey() = delete; + ContactKey(void* a, void* b) : _a(a), _b(b) {} + bool operator<(const ContactKey& other) const { return _a < other._a || (_a == other._a && _b < other._b); } + void* _a; + void* _b; +}; + +typedef std::map ContactMap; + class PhysicsEngine : public EntitySimulation { public: // TODO: find a good way to make this a non-static method @@ -73,6 +86,7 @@ public: void relayIncomingChangesToSimulation(); private: + void removeContacts(ObjectMotionState* motionState); void updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); @@ -92,7 +106,8 @@ private: QSet _outgoingPackets; // MotionStates with pending changes that need to be sent over wire EntityEditPacketSender* _entityPacketSender = NULL; - uint32_t _numSteps = 0; // do not confuse with static _numSubsteps + + ContactMap _contactMap; }; #endif // hifi_PhysicsEngine_h From e95cd30be6272c2f9e967a9b734239bbc1fd806f Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 20 Jan 2015 16:30:50 -0800 Subject: [PATCH 05/59] better reticle, balls drop properly on table --- examples/billiards.js | 51 +++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/examples/billiards.js b/examples/billiards.js index 9bc21ff611..d671addbe6 100644 --- a/examples/billiards.js +++ b/examples/billiards.js @@ -1,6 +1,7 @@ // Pool Table var tableParts = []; var balls = []; +var cueBall; var LENGTH = 2.84; var WIDTH = 1.42; @@ -13,6 +14,8 @@ var HOLE_SIZE = BALL_SIZE; var DROP_HEIGHT = BALL_SIZE * 3.0; var GRAVITY = -9.8; var BALL_GAP = 0.001; +var tableCenter; +var cuePosition; var startStroke = 0; @@ -23,7 +26,7 @@ var reticle = Overlays.addOverlay("image", { y: screenSize.y / 2 - 16, width: 32, height: 32, - imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png", + imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png", color: { red: 255, green: 255, blue: 255}, alpha: 1 }); @@ -102,7 +105,7 @@ function makeBalls(pos) { { red: 128, green: 128, blue: 128}]; // Gray // Object balls - var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z }; + var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z }; for (var row = 1; row <= 5; row++) { ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE); for (var spot = 0; spot < row; spot++) { @@ -113,23 +116,26 @@ function makeBalls(pos) { color: colors[balls.length], gravity: { x: 0, y: GRAVITY, z: 0 }, ignoreCollisions: false, - damping: 0.40, + damping: 0.50, collisionsWillMove: true })); ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE; } ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE; } // Cue Ball - ballPosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z }; - balls.push(Entities.addEntity( + cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z }; + cueBall = Entities.addEntity( { type: "Sphere", - position: ballPosition, + position: cuePosition, dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, color: { red: 255, green: 255, blue: 255 }, gravity: { x: 0, y: GRAVITY, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0 }, + velocity: {x: 0, y: 0, z: 0 }, ignoreCollisions: false, - damping: 0.40, - collisionsWillMove: true })); + damping: 0.50, + collisionsWillMove: true }); + } function shootCue(velocity) { @@ -140,19 +146,23 @@ function shootCue(velocity) { var velocity = Vec3.multiply(forwardVector, velocity); var BULLET_LIFETIME = 3.0; var BULLET_GRAVITY = 0.0; + var SHOOTER_COLOR = { red: 255, green: 0, blue: 0 }; + var SHOOTER_SIZE = BALL_SIZE / 1.5 * SCALE; + bulletID = Entities.addEntity( { type: "Sphere", position: cuePosition, - dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, - color: { red: 255, green: 255, blue: 255 }, + dimensions: { x: SHOOTER_SIZE, y: SHOOTER_SIZE, z: SHOOTER_SIZE }, + color: SHOOTER_COLOR, velocity: velocity, lifetime: BULLET_LIFETIME, gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, damping: 0.10, - density: 1000, + density: 8000, ignoreCollisions: false, collisionsWillMove: true }); + print("Shot, velocity = " + velocity); } function keyReleaseEvent(event) { @@ -185,9 +195,25 @@ function cleanup() { Entities.deleteEntity(balls[i]); } Overlays.deleteOverlay(reticle); + Entities.deleteEntity(cueBall); } -var tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation()))); +function update(deltaTime) { + if (!cueBall.isKnownID) { + cueBall = Entities.identifyEntity(cueBall); + } else { + // Check if cue ball has fallen off table, re-drop if so + var cueProperties = Entities.getEntityProperties(cueBall); + if (cueProperties.position.y < tableCenter.y) { + // Replace the cueball + Entities.editEntity(cueBall, { position: cuePosition } ); + + } + } + +} + +tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation()))); makeTable(tableCenter); makeBalls(tableCenter); @@ -195,3 +221,4 @@ makeBalls(tableCenter); Script.scriptEnding.connect(cleanup); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.update.connect(update); From e82b07caf827af3c9c7a975ecc72ced33e319125 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 21 Jan 2015 11:26:08 -0600 Subject: [PATCH 06/59] Updated Windows build instructions --- BUILD_WIN.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index cf8308c552..883b30b085 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -132,6 +132,35 @@ This package contains only headers, so there's nothing to add to the PATH. Be careful with glm. For the folder other libraries would normally call 'include', the folder containing the headers, glm opts to use 'glm'. You will have a glm folder nested inside the top-level glm folder. +###Bullet + +Bullet 2.82 source can be downloaded [here](https://code.google.com/p/bullet/downloads/detail?name=bullet-2.82-r2704.zip). Bullet does not come with prebuilt libraries, you need to make those yourself. + +* Download the zip file and extract into a temporary folder +* Create a directory named cmakebuild. Bullet comes with a build\ directory by default, however, that directory is intended for use with premake, and considering premake doesn't support VS2013, I prefer to run the cmake build on its own directory. +* Make the following modifications to Bullet's source: + 1. In file: Extras\HACD\hacdICHull.cpp --- in line: 17 --- insert: #include + 2. In file: src\MiniCL\cl_MiniCL_Defs.h --- comment lines 364 to 372 + 3. In file: CMakeLists.txt set to ON the option USE_MSVC_RUNTIME_LIBRARY_DLL in line 27 + +Then create the Visual Studio solution and build the libraries - run the following commands from a Visual Studio 2013 command prompt, from within the cmakebuild directory created before: + +```shell +cmake .. -G "Visual Studio 12" +msbuild BULLET_PHYSICS.sln /p:Configuration=Debug +``` + +This will create Debug libraries in cmakebuild\lib\Debug you can replace Debug with Release in the msbuild command and that will generate Release libraries in cmakebuild\lib\Release. + +You now have Bullet libraries compiled, now you need to put them in the right place for hifi to find them: + +* Create a directory named bullet\ inside your %HIFI_LIB_DIR% +* Create two directores named lib\ and include\ inside bullet\ +* Copy all the contents inside src\ from the bullet unzip path into %HIFI_LIB_DIR%\bullet\include\ +* Copy all the contents inside cmakebuild\lib\ into %HIFI_LIB_DIR\bullet\lib + +*Note that the INSTALL target should handle the copying of files into an install directory automatically, however, without modifications to Cmake, the install target didn't work right for me, please update this instructions if you get that working right - Leo " + ###Build High Fidelity using Visual Studio Follow the same build steps from the CMake section, but pass a different generator to CMake. From 93edd8203875fd0d27544449ecdedeede212c987 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 21 Jan 2015 11:28:50 -0600 Subject: [PATCH 07/59] Markdown fix --- BUILD_WIN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 883b30b085..c12724a88f 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -159,7 +159,7 @@ You now have Bullet libraries compiled, now you need to put them in the right pl * Copy all the contents inside src\ from the bullet unzip path into %HIFI_LIB_DIR%\bullet\include\ * Copy all the contents inside cmakebuild\lib\ into %HIFI_LIB_DIR\bullet\lib -*Note that the INSTALL target should handle the copying of files into an install directory automatically, however, without modifications to Cmake, the install target didn't work right for me, please update this instructions if you get that working right - Leo " +_Note that the INSTALL target should handle the copying of files into an install directory automatically, however, without modifications to Cmake, the install target didn't work right for me, please update this instructions if you get that working right - Leo _ ###Build High Fidelity using Visual Studio Follow the same build steps from the CMake section, but pass a different generator to CMake. From a825f2a4953c51684734e6d778c122c5373d6015 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 21 Jan 2015 11:31:24 -0600 Subject: [PATCH 08/59] Markdown fix --- BUILD_WIN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index c12724a88f..3ccc4881c1 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -139,7 +139,7 @@ Bullet 2.82 source can be downloaded [here](https://code.google.com/p/bullet/dow * Download the zip file and extract into a temporary folder * Create a directory named cmakebuild. Bullet comes with a build\ directory by default, however, that directory is intended for use with premake, and considering premake doesn't support VS2013, I prefer to run the cmake build on its own directory. * Make the following modifications to Bullet's source: - 1. In file: Extras\HACD\hacdICHull.cpp --- in line: 17 --- insert: #include + 1. In file: Extras\HACD\hacdICHull.cpp --- in line: 17 --- insert: #include <algorithm> 2. In file: src\MiniCL\cl_MiniCL_Defs.h --- comment lines 364 to 372 3. In file: CMakeLists.txt set to ON the option USE_MSVC_RUNTIME_LIBRARY_DLL in line 27 @@ -159,7 +159,7 @@ You now have Bullet libraries compiled, now you need to put them in the right pl * Copy all the contents inside src\ from the bullet unzip path into %HIFI_LIB_DIR%\bullet\include\ * Copy all the contents inside cmakebuild\lib\ into %HIFI_LIB_DIR\bullet\lib -_Note that the INSTALL target should handle the copying of files into an install directory automatically, however, without modifications to Cmake, the install target didn't work right for me, please update this instructions if you get that working right - Leo _ +_Note that the INSTALL target should handle the copying of files into an install directory automatically, however, without modifications to Cmake, the install target didn't work right for me, please update this instructions if you get that working right - Leo <leo@highfidelity.io>_ ###Build High Fidelity using Visual Studio Follow the same build steps from the CMake section, but pass a different generator to CMake. From 86583f3f3f3cb1a5c714536bfdee9b848011c30a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 21 Jan 2015 11:27:32 -0800 Subject: [PATCH 09/59] physics collisions emit script collision events --- interface/src/Application.cpp | 21 +++-------- interface/src/Application.h | 1 - .../entities/src/EntityCollisionSystem.cpp | 13 ++----- .../entities/src/EntityCollisionSystem.h | 9 +---- libraries/entities/src/EntitySimulation.h | 7 +++- libraries/physics/src/ContactInfo.cpp | 29 +++++++++++++++ libraries/physics/src/ContactInfo.h | 37 +++++++++++++++++++ libraries/physics/src/EntityMotionState.cpp | 1 + libraries/physics/src/EntityMotionState.h | 2 + libraries/physics/src/ObjectMotionState.cpp | 8 ---- libraries/physics/src/ObjectMotionState.h | 11 +++++- libraries/physics/src/PhysicsEngine.cpp | 31 +++++++++++----- libraries/physics/src/PhysicsEngine.h | 2 + 13 files changed, 119 insertions(+), 53 deletions(-) create mode 100644 libraries/physics/src/ContactInfo.cpp create mode 100644 libraries/physics/src/ContactInfo.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4dfe8313c6..78fd0f943e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -192,7 +192,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _justStarted(true), _physicsEngine(glm::vec3(0.0f)), _entities(true, this, this), - _entityCollisionSystem(), _entityClipboardRenderer(false, this, this), _entityClipboard(), _viewFrustum(), @@ -1689,17 +1688,16 @@ void Application::init() { _entities.init(); _entities.setViewFrustum(getViewFrustum()); - EntityTree* entityTree = _entities.getTree(); - - _entityCollisionSystem.init(&_entityEditSender, entityTree, &_avatarManager); - - entityTree->setSimulation(&_entityCollisionSystem); + EntityTree* tree = _entities.getTree(); + _physicsEngine.setEntityTree(tree); + tree->setSimulation(&_physicsEngine); + _physicsEngine.init(&_entityEditSender); - connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithEntity, + connect(&_physicsEngine, &EntitySimulation::emitEntityCollisionWithEntity, ScriptEngine::getEntityScriptingInterface(), &EntityScriptingInterface::entityCollisionWithEntity); // connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts - connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithEntity, + connect(&_physicsEngine, &EntitySimulation::emitEntityCollisionWithEntity, &_entities, &EntityTreeRenderer::entityCollisionWithEntity); // connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing @@ -1723,10 +1721,6 @@ void Application::init() { // save settings when avatar changes connect(_myAvatar, &MyAvatar::transformChanged, this, &Application::bumpSettings); - EntityTree* tree = _entities.getTree(); - _physicsEngine.setEntityTree(tree); - tree->setSimulation(&_physicsEngine); - _physicsEngine.init(&_entityEditSender); // make sure our texture cache knows about window size changes DependencyManager::get()->associateWithWidget(glCanvas.data()); @@ -2047,9 +2041,6 @@ void Application::update(float deltaTime) { // NOTE: the _entities.update() call below will wait for lock // and will simulate entity motion (the EntityTree has been given an EntitySimulation). _entities.update(); // update the models... - // The _entityCollisionSystem.updateCollisions() call below merely tries for lock, - // and on failure it skips collision detection. - _entityCollisionSystem.updateCollisions(); // collide the entities... } { diff --git a/interface/src/Application.h b/interface/src/Application.h index a875c6b9fe..c79cf8a989 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -466,7 +466,6 @@ private: PhysicsEngine _physicsEngine; EntityTreeRenderer _entities; - EntityCollisionSystem _entityCollisionSystem; EntityTreeRenderer _entityClipboardRenderer; EntityTree _entityClipboard; diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 172850bc64..12d4e9e61e 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -62,14 +62,6 @@ void EntityCollisionSystem::checkEntity(EntityItem* entity) { updateCollisionWithAvatars(entity); } -void EntityCollisionSystem::emitGlobalEntityCollisionWithEntity(EntityItem* entityA, - EntityItem* entityB, const Collision& collision) { - - EntityItemID idA = entityA->getEntityItemID(); - EntityItemID idB = entityB->getEntityItemID(); - emit entityCollisionWithEntity(idA, idB, collision); -} - void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { if (entityA->getIgnoreForCollisions()) { @@ -193,7 +185,10 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { Collision collision; collision.penetration = penetration; collision.contactPoint = (0.5f * (float)TREE_SCALE) * (entityA->getPosition() + entityB->getPosition()); - emitGlobalEntityCollisionWithEntity(entityA, entityB, collision); + + EntityItemID idA = entityA->getEntityItemID(); + EntityItemID idB = entityB->getEntityItemID(); + emitEntityCollisionWithEntity(idA, idB, collision); } } } diff --git a/libraries/entities/src/EntityCollisionSystem.h b/libraries/entities/src/EntityCollisionSystem.h index ce7d57bef7..d6cafd88f3 100644 --- a/libraries/entities/src/EntityCollisionSystem.h +++ b/libraries/entities/src/EntityCollisionSystem.h @@ -31,8 +31,7 @@ class AvatarData; class EntityEditPacketSender; class EntityTree; -class EntityCollisionSystem : public QObject, public SimpleEntitySimulation { -Q_OBJECT +class EntityCollisionSystem : public SimpleEntitySimulation { public: EntityCollisionSystem(); @@ -45,19 +44,13 @@ public: void checkEntity(EntityItem* Entity); void updateCollisionWithEntities(EntityItem* Entity); void updateCollisionWithAvatars(EntityItem* Entity); - void queueEntityPropertiesUpdate(EntityItem* Entity); - -signals: - void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); private: void applyHardCollision(EntityItem* entity, const CollisionInfo& collisionInfo); static bool updateOperation(OctreeElement* element, void* extraData); - void emitGlobalEntityCollisionWithEntity(EntityItem* entityA, EntityItem* entityB, const Collision& penetration); EntityEditPacketSender* _packetSender; - AbstractAudioInterface* _audio; AvatarHashMap* _avatars; CollisionList _collisions; }; diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index dee6b1c43a..a7e18b67df 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -12,6 +12,7 @@ #ifndef hifi_EntitySimulation_h #define hifi_EntitySimulation_h +#include #include #include @@ -31,7 +32,8 @@ const int DIRTY_SIMULATION_FLAGS = EntityItem::DIRTY_LIFETIME | EntityItem::DIRTY_UPDATEABLE; -class EntitySimulation { +class EntitySimulation : public QObject { +Q_OBJECT public: EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL) { } virtual ~EntitySimulation() { setEntityTree(NULL); } @@ -61,6 +63,9 @@ public: EntityTree* getEntityTree() { return _entityTree; } +signals: + void emitEntityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); + protected: // These pure virtual methods are protected because they are not to be called will-nilly. The base class diff --git a/libraries/physics/src/ContactInfo.cpp b/libraries/physics/src/ContactInfo.cpp new file mode 100644 index 0000000000..71887b2dc9 --- /dev/null +++ b/libraries/physics/src/ContactInfo.cpp @@ -0,0 +1,29 @@ +// +// ContactEvent.cpp +// libraries/physcis/src +// +// Created by Andrew Meadows 2015.01.20 +// Copyright 2015 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 "BulletUtil.h" +#include "ContactInfo.h" + +void ContactInfo::update(uint32_t currentStep, btManifoldPoint& p, const glm::vec3& worldOffset) { + _lastStep = currentStep; + ++_numSteps; + contactPoint = bulletToGLM(p.m_positionWorldOnB) + worldOffset; + penetration = bulletToGLM(p.m_distance1 * p.m_normalWorldOnB); + // TODO: also report normal + //_normal = bulletToGLM(p.m_normalWorldOnB); +} + +ContactEventType ContactInfo::computeType(uint32_t thisStep) { + if (_lastStep != thisStep) { + return CONTACT_EVENT_TYPE_END; + } + return (_numSteps == 1) ? CONTACT_EVENT_TYPE_START : CONTACT_EVENT_TYPE_CONTINUE; +} diff --git a/libraries/physics/src/ContactInfo.h b/libraries/physics/src/ContactInfo.h new file mode 100644 index 0000000000..2c3c3a1e6f --- /dev/null +++ b/libraries/physics/src/ContactInfo.h @@ -0,0 +1,37 @@ +// +// ContactEvent.h +// libraries/physcis/src +// +// Created by Andrew Meadows 2015.01.20 +// Copyright 2015 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_ContactEvent_h +#define hifi_ContactEvent_h + +#include +#include + +#include "RegisteredMetaTypes.h" + +enum ContactEventType { + CONTACT_EVENT_TYPE_START, + CONTACT_EVENT_TYPE_CONTINUE, + CONTACT_EVENT_TYPE_END +}; + +class ContactInfo : public Collision +{ +public: + void update(uint32_t currentStep, btManifoldPoint& p, const glm::vec3& worldOffset); + ContactEventType computeType(uint32_t thisStep); +private: + uint32_t _lastStep = 0; + uint32_t _numSteps = 0; +}; + + +#endif // hifi_ContactEvent_h diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9ff4a9d0e1..8b6fb1ea9f 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -33,6 +33,7 @@ void EntityMotionState::enqueueOutgoingEntity(EntityItem* entity) { EntityMotionState::EntityMotionState(EntityItem* entity) : _entity(entity) { + _type = MOTION_STATE_TYPE_ENTITY; assert(entity != NULL); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index da06d46451..8eb639688a 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -60,6 +60,8 @@ public: uint32_t getIncomingDirtyFlags() const; void clearIncomingDirtyFlags(uint32_t flags) { _entity->clearDirtyFlags(flags); } + EntityItem* getEntity() const { return _entity; } + protected: EntityItem* _entity; }; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 929ddab3e0..cab36b8370 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -171,14 +171,6 @@ void ObjectMotionState::removeKinematicController() { } } -void ObjectMotionState::handleContactEvent(ContactEventType type, ObjectMotionState* otherState) { - if (type == CONTACT_TYPE_START) { - std::cout << "adebug start " << (void*)(this) << " vs " << (void*)(otherState) << std::endl; // adebug - } else if (type == CONTACT_TYPE_END) { - std::cout << "adebug end " << (void*)(this) << " vs " << (void*)(otherState) << std::endl; // adebug - } -} - void ObjectMotionState::setRigidBody(btRigidBody* body) { // give the body a (void*) back-pointer to this ObjectMotionState if (_body != body) { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 2eda618d93..223664ceec 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -26,6 +26,12 @@ enum MotionType { MOTION_TYPE_KINEMATIC // keyframed motion }; +enum MotionStateType { + MOTION_STATE_TYPE_UNKNOWN, + MOTION_STATE_TYPE_ENTITY, + MOTION_STATE_TYPE_AVATAR +}; + // The update flags trigger two varieties of updates: "hard" which require the body to be pulled // and re-added to the physics engine and "easy" which just updates the body properties. const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE); @@ -59,6 +65,7 @@ public: virtual void updateObjectEasy(uint32_t flags, uint32_t frame) = 0; virtual void updateObjectVelocities() = 0; + MotionStateType getType() const { return _type; } virtual MotionType getMotionType() const { return _motionType; } virtual void computeShapeInfo(ShapeInfo& info) = 0; @@ -89,14 +96,14 @@ public: virtual void addKinematicController() = 0; virtual void removeKinematicController(); - virtual void handleContactEvent(ContactEventType type, ObjectMotionState* otherState); - btRigidBody* getRigidBody() const { return _body; } friend class PhysicsEngine; protected: void setRigidBody(btRigidBody* body); + MotionStateType _type = MOTION_STATE_TYPE_UNKNOWN; + // TODO: move these materials properties outside of ObjectMotionState float _friction; float _restitution; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 5a2881da71..ca14261c3b 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -256,8 +256,9 @@ void PhysicsEngine::computeCollisionEvents() { void* a = objectA->getUserPointer(); void* b = objectB->getUserPointer(); - if (a || b ) { - _contactMap[ContactKey(a, b)].update(_numSubsteps); + if (a || b) { + // the manifold has up to 4 distinct points, but only extract info from the first + _contactMap[ContactKey(a, b)].update(_numSubsteps, contactManifold->getContactPoint(0), _originOffset); } } } @@ -265,16 +266,28 @@ void PhysicsEngine::computeCollisionEvents() { // scan known contacts and trigger events ContactMap::iterator contactItr = _contactMap.begin(); while (contactItr != _contactMap.end()) { - ContactEventType type = contactItr->second.computeType(_numSubsteps); ObjectMotionState* A = static_cast(contactItr->first._a); ObjectMotionState* B = static_cast(contactItr->first._b); - if (A) { - A->handleContactEvent(type, B); + + // TODO: make triggering these events clean and efficient. The code at this context shouldn't + // have to figure out what kind of object (entity, avatar, etc) these are in order to properly + // emit a collision event. + if (A && A->getType() == MOTION_STATE_TYPE_ENTITY) { + EntityItemID idA = static_cast(A)->getEntity()->getEntityItemID(); + EntityItemID idB; + if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) { + idB = static_cast(B)->getEntity()->getEntityItemID(); + } + emitEntityCollisionWithEntity(idA, idB, contactItr->second); + } else if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) { + EntityItemID idA; + EntityItemID idB = static_cast(B)->getEntity()->getEntityItemID(); + emitEntityCollisionWithEntity(idA, idB, contactItr->second); } - if (B) { - B->handleContactEvent(type, A); - } - if (type == CONTACT_TYPE_END) { + + // TODO: enable scripts to filter based on contact event type + ContactEventType type = contactItr->second.computeType(_numSubsteps); + if (type == CONTACT_EVENT_TYPE_END) { ContactMap::iterator iterToDelete = contactItr; ++contactItr; _contactMap.erase(iterToDelete); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 9db9f5a33a..fdd4107421 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -38,11 +38,13 @@ public: ContactKey() = delete; ContactKey(void* a, void* b) : _a(a), _b(b) {} bool operator<(const ContactKey& other) const { return _a < other._a || (_a == other._a && _b < other._b); } + bool operator==(const ContactKey& other) const { return _a == other._a && _b == other._b; } void* _a; void* _b; }; typedef std::map ContactMap; +typedef std::pair ContactMapElement; class PhysicsEngine : public EntitySimulation { public: From 6840daa0b5dbd697bea72328ddff2896bfc5e9a8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 21 Jan 2015 11:55:28 -0800 Subject: [PATCH 10/59] removing tabs from formatting --- libraries/physics/src/PhysicsEngine.cpp | 42 ++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index a8b168681e..dea321be22 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -159,14 +159,14 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { void PhysicsEngine::removeContacts(ObjectMotionState* motionState) { // trigger events for new/existing/old contacts - ContactMap::iterator contactItr = _contactMap.begin(); - while (contactItr != _contactMap.end()) { + ContactMap::iterator contactItr = _contactMap.begin(); + while (contactItr != _contactMap.end()) { if (contactItr->first._a == motionState || contactItr->first._b == motionState) { - ContactMap::iterator iterToDelete = contactItr; - ++contactItr; - _contactMap.erase(iterToDelete); + ContactMap::iterator iterToDelete = contactItr; + ++contactItr; + _contactMap.erase(iterToDelete); } else { - ++contactItr; + ++contactItr; } } } @@ -251,26 +251,26 @@ void PhysicsEngine::stepSimulation() { void PhysicsEngine::computeCollisionEvents() { // update all contacts int numManifolds = _collisionDispatcher->getNumManifolds(); - for (int i = 0; i < numManifolds; ++i) { - btPersistentManifold* contactManifold = _collisionDispatcher->getManifoldByIndexInternal(i); + for (int i = 0; i < numManifolds; ++i) { + btPersistentManifold* contactManifold = _collisionDispatcher->getManifoldByIndexInternal(i); if (contactManifold->getNumContacts() > 0) { - const btCollisionObject* objectA = static_cast(contactManifold->getBody0()); - const btCollisionObject* objectB = static_cast(contactManifold->getBody1()); + const btCollisionObject* objectA = static_cast(contactManifold->getBody0()); + const btCollisionObject* objectB = static_cast(contactManifold->getBody1()); void* a = objectA->getUserPointer(); void* b = objectB->getUserPointer(); if (a || b) { // the manifold has up to 4 distinct points, but only extract info from the first - _contactMap[ContactKey(a, b)].update(_numSubsteps, contactManifold->getContactPoint(0), _originOffset); + _contactMap[ContactKey(a, b)].update(_numSubsteps, contactManifold->getContactPoint(0), _originOffset); } } } - + // scan known contacts and trigger events - ContactMap::iterator contactItr = _contactMap.begin(); - while (contactItr != _contactMap.end()) { - ObjectMotionState* A = static_cast(contactItr->first._a); - ObjectMotionState* B = static_cast(contactItr->first._b); + ContactMap::iterator contactItr = _contactMap.begin(); + while (contactItr != _contactMap.end()) { + ObjectMotionState* A = static_cast(contactItr->first._a); + ObjectMotionState* B = static_cast(contactItr->first._b); // TODO: make triggering these events clean and efficient. The code at this context shouldn't // have to figure out what kind of object (entity, avatar, etc) these are in order to properly @@ -289,13 +289,13 @@ void PhysicsEngine::computeCollisionEvents() { } // TODO: enable scripts to filter based on contact event type - ContactEventType type = contactItr->second.computeType(_numSubsteps); + ContactEventType type = contactItr->second.computeType(_numSubsteps); if (type == CONTACT_EVENT_TYPE_END) { - ContactMap::iterator iterToDelete = contactItr; - ++contactItr; - _contactMap.erase(iterToDelete); + ContactMap::iterator iterToDelete = contactItr; + ++contactItr; + _contactMap.erase(iterToDelete); } else { - ++contactItr; + ++contactItr; } } } From 87e33397918d1148684210c93fe2692feb04e07f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 Jan 2015 12:28:05 -0800 Subject: [PATCH 11/59] Fix for normal selection with degenerate triangles. --- interface/src/MetavoxelSystem.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index eeb1c84a4e..ea4941ee33 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1498,6 +1498,17 @@ const NormalIndex& IndexVector::get(int y) const { return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex; } +static inline glm::vec3 getNormal(const QVector& vertices, const NormalIndex& i0, + const NormalIndex& i1, const NormalIndex& i2, const NormalIndex& i3) { + // check both triangles in case one is degenerate + const glm::vec3& v0 = vertices.at(i0.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(i1.indices[0]).vertex - v0, vertices.at(i2.indices[0]).vertex - v0); + if (glm::length(normal) > EPSILON) { + return normal; + } + return glm::cross(vertices.at(i2.indices[0]).vertex - v0, vertices.at(i3.indices[0]).vertex - v0); +} + void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { if (!node->getHeight()) { @@ -2174,10 +2185,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size()); quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); } - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - + glm::vec3 normal = getNormal(vertices, index, index1, index2, index3); if (alpha0 == 0) { // quad faces negative x indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); @@ -2206,10 +2214,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (reclampedZ > 0) { quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); } - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, - vertices.at(index1.indices[0]).vertex - first); - + glm::vec3 normal = getNormal(vertices, index, index3, index2, index1); if (alpha0 == 0) { // quad faces negative y indices.append(index3.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); @@ -2235,10 +2240,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - + glm::vec3 normal = getNormal(vertices, index, index1, index2, index3); if (alpha0 == 0) { // quad faces negative z indices.append(index1.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); From 2171427bbdff69e5861c55cdfa822f895b344746 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Jan 2015 12:31:06 -0800 Subject: [PATCH 12/59] Update style.css to use pt instead of px for proper rendering on hidpi screens --- examples/html/style.css | 66 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/examples/html/style.css b/examples/html/style.css index ad78d0234c..08ca32aba5 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -8,7 +8,7 @@ body { background-color: rgb(76, 76, 76); color: rgb(204, 204, 204); font-family: Arial; - font-size: 11px; + font-size: 8.25pt; -webkit-touch-callout: none; -webkit-user-select: none; @@ -31,25 +31,25 @@ body { .color-box { display: inline-block; - width: 20px; - height: 20px; - border: 1px solid black; - margin: 2px; + width: 15pt; + height: 15pt; + border: 0.75pt solid black; + margin: 1.5pt; cursor: pointer; } .color-box.highlight { - width: 18px; - height: 18px; - border: 2px solid black; + width: 13.5pt; + height: 13.5pt; + border: 1.5pt solid black; } .section-header { background: #AAA; - border-bottom: 1px solid #CCC; + border-bottom: 0.75pt solid #CCC; background-color: #333333; color: #999; - padding: 4px; + padding: 3pt; } .section-header label { @@ -61,7 +61,7 @@ body { .property-section { display: block; margin: 10 10; - height: 30px; + height: 22.5pt; } .property-section label { @@ -73,7 +73,7 @@ body { } .grid-section { - border-top: 1px solid #DDD; + border-top: 0.75pt solid #DDD; background-color: #efefef; } @@ -81,8 +81,8 @@ input[type=button] { cursor: pointer; background-color: #608e96; border-color: #608e96; - border-radius: 5px; - padding: 5px 10px; + border-radius: 3.75pt; + padding: 3.75pt 7.5pt; border: 0; color: #fff; font-weight: bold; @@ -90,8 +90,8 @@ input[type=button] { textarea, input { margin: 0; - padding: 2px; - border: 1px solid #999; + padding: 1.5pt; + border: 0.75pt solid #999; background-color: #eee; } @@ -112,13 +112,13 @@ input.coord { table#entity-table { border-collapse: collapse; font-family: Sans-Serif; - font-size: 10px; + font-size: 7.5pt; width: 100%; } #entity-table tr { cursor: pointer; - border-bottom: 1px solid rgb(63, 63, 63) + border-bottom: 0.75pt solid rgb(63, 63, 63) } #entity-table tr.selected { @@ -128,15 +128,15 @@ table#entity-table { #entity-table th { background-color: #333; color: #fff; - border: 0px black solid; + border: 0pt black solid; text-align: left; word-wrap: nowrap; white-space: nowrap; } #entity-table td { - font-size: 11px; - border: 0px black solid; + font-size: 8.25pt; + border: 0pt black solid; word-wrap: nowrap; white-space: nowrap; text-overflow: ellipsis; @@ -148,21 +148,21 @@ table#entity-table { } th#entity-type { - width: 60px; + width: 33.75pt; } div.input-area { display: inline-block; - font-size: 10px; + font-size: 7.5pt; } input { } #type { - font-size: 14px; + font-size: 10.5pt; } #type label { @@ -173,22 +173,22 @@ input { background-color: rgb(102, 102, 102); color: rgb(204, 204, 204); border: none; - font-size: 10px; + font-size: 7.5pt; } #properties-list input[type=button] { cursor: pointer; background-color: rgb(51, 102, 102); border-color: #608e96; - border-radius: 5px; - padding: 5px 10px; + border-radius: 3.75pt; + padding: 3.75pt 7.5pt; border: 0; color: rgb(204, 204, 204); } #properties-list .property { - padding: 8px 8px; - border-top: 1px solid rgb(63, 63, 63); + padding: 6pt 6pt; + border-top: 0.75pt solid rgb(63, 63, 63); min-height: 1em; } @@ -203,11 +203,11 @@ table#properties-list { } #properties-list > div { - margin: 4px 0; + margin: 3pt 0; } #properties-list { - border-bottom: 1px solid #e5e5e5; + border-bottom: 0.75pt solid #e5e5e5; } #properties-list .label { @@ -221,11 +221,11 @@ table#properties-list { } #properties-list .value > div{ - padding: 4px 0; + padding: 3pt 0; } col#col-label { - width: 130px; + width: 97.5pt; } div.outer { From 8636f0c48e80374ea3614789a8a9e3f5c6d951f4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 Jan 2015 12:57:24 -0800 Subject: [PATCH 13/59] Simplification. --- interface/src/MetavoxelSystem.cpp | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index ea4941ee33..719326f628 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1857,11 +1857,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (!(corners & (1 << i))) { continue; } - int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; - int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; - const quint16* height = heightLineSrc + offsetZ * width + offsetX; - float heightValue = *height * voxelScale; - if (heightValue >= y && heightValue < y + 1) { + const EdgeCrossing& cornerCrossing = cornerCrossings[i]; + if (cornerCrossing.point.y >= y && cornerCrossing.point.y < y + 1) { crossedCorners |= (1 << i); } } @@ -1901,30 +1898,24 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (!(corners & (1 << i))) { continue; } - int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; - int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; - const quint16* height = heightLineSrc + offsetZ * width + offsetX; - float heightValue = *height * voxelScale; int nextIndex = NEXT_CORNERS[i]; if (!(corners & (1 << nextIndex))) { continue; } - int nextOffsetX = (nextIndex & X_MAXIMUM_FLAG) ? 1 : 0; - int nextOffsetZ = (nextIndex & Y_MAXIMUM_FLAG) ? 1 : 0; - const quint16* nextHeight = heightLineSrc + nextOffsetZ * width + nextOffsetX; - float nextHeightValue = *nextHeight * voxelScale; - float divisor = (nextHeightValue - heightValue); + const EdgeCrossing& cornerCrossing = cornerCrossings[i]; + const EdgeCrossing& nextCornerCrossing = cornerCrossings[nextIndex]; + float divisor = (nextCornerCrossing.point.y - cornerCrossing.point.y); if (divisor == 0.0f) { continue; } - float t1 = (y - heightValue) / divisor; - float t2 = (y + 1 - heightValue) / divisor; + float t1 = (y - cornerCrossing.point.y) / divisor; + float t2 = (y + 1 - cornerCrossing.point.y) / divisor; if (t1 >= 0.0f && t1 <= 1.0f) { - crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t1); + crossings[crossingCount++].mix(cornerCrossing, nextCornerCrossing, t1); crossings[crossingCount - 1].point.y -= y; } if (t2 >= 0.0f && t2 <= 1.0f) { - crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t2); + crossings[crossingCount++].mix(cornerCrossing, nextCornerCrossing, t2); crossings[crossingCount - 1].point.y -= y; } } From e02e0088afdf911bdb79da43b4192a61b842efea Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 21 Jan 2015 13:58:48 -0800 Subject: [PATCH 14/59] Improvements to gun to add shootable targets on a platform --- examples/controllers/hydra/gun.js | 60 ++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index e3450b708e..d190fff503 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -47,7 +47,6 @@ var yawFromMouse = 0; var pitchFromMouse = 0; var isMouseDown = false; -var BULLET_VELOCITY = 5.0; var MIN_THROWER_DELAY = 1000; var MAX_THROWER_DELAY = 1000; var LEFT_BUTTON_3 = 3; @@ -98,7 +97,7 @@ var reticle = Overlays.addOverlay("image", { y: screenSize.y / 2 - 16, width: 32, height: 32, - imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png", + imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png", color: { red: 255, green: 255, blue: 255}, alpha: 1 }); @@ -131,10 +130,12 @@ function printVector(string, vector) { print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); } +var BULLET_VELOCITY = 15.0; + function shootBullet(position, velocity) { var BULLET_SIZE = 0.07; var BULLET_LIFETIME = 10.0; - var BULLET_GRAVITY = 0.0; + var BULLET_GRAVITY = -0.25; bulletID = Entities.addEntity( { type: "Sphere", position: position, @@ -144,7 +145,7 @@ function shootBullet(position, velocity) { lifetime: BULLET_LIFETIME, gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, damping: 0.01, - density: 5000, + density: 10000, ignoreCollisions: false, collisionsWillMove: true }); @@ -158,6 +159,9 @@ function shootBullet(position, velocity) { } // Kickback the arm + if (elbowKickAngle > 0.0) { + MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback); + } rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm"); var armRotation = MyAvatar.getJointRotation("LeftForeArm"); armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE)); @@ -165,6 +169,50 @@ function shootBullet(position, velocity) { elbowKickAngle = KICKBACK_ANGLE; } +function makeGrid(type, gravity, scale, size) { + var separation = scale * 2; + var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); + pos.y -= separation * size; + var x, y, z; + var GRID_LIFE = 60.0; + var dimensions; + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + for (z = 0; z < size; z++) { + if (gravity == 0) { + dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 }; + } else { + dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/4.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) }; + } + + Entities.addEntity( + { type: type, + position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation }, + dimensions: dimensions, + color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: gravity, z: 0 }, + lifetime: GRID_LIFE, + rotation: Camera.getOrientation(), + damping: 0.1, + density: 100.0, + collisionsWillMove: true }); + } + } + } + if (gravity < 0) { + // Make a floor for this stuff to fall onto + Entities.addEntity({ + type: "Box", + position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, + dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size }, + color: { red: 128, green: 128, blue: 128 }, + lifetime: GRID_LIFE + }); + } +} + function shootTarget() { var TARGET_SIZE = 0.50; var TARGET_GRAVITY = 0.0; @@ -236,6 +284,10 @@ function keyPressEvent(event) { shootFromMouse(); } else if (event.text == "r") { playLoadSound(); + } else if (event.text == "g") { + makeGrid("Box", 0.0, 0.4, 3); + } else if (event.text == "f") { + makeGrid("Box", -9.8, 0.4, 5); } else if (event.text == "s") { // Hit this key to dump a posture from hydra to log Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm")); From 1c38b08e8f25d5452e623ea20a07a40648f8f49d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 21 Jan 2015 14:07:23 -0800 Subject: [PATCH 15/59] remane emitEntityCollision... to entityCollision... --- interface/src/Application.cpp | 4 ++-- libraries/entities/src/EntityCollisionSystem.cpp | 2 +- libraries/entities/src/EntitySimulation.h | 2 +- libraries/physics/src/PhysicsEngine.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 78fd0f943e..86af8b0e91 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1693,11 +1693,11 @@ void Application::init() { tree->setSimulation(&_physicsEngine); _physicsEngine.init(&_entityEditSender); - connect(&_physicsEngine, &EntitySimulation::emitEntityCollisionWithEntity, + connect(&_physicsEngine, &EntitySimulation::entityCollisionWithEntity, ScriptEngine::getEntityScriptingInterface(), &EntityScriptingInterface::entityCollisionWithEntity); // connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts - connect(&_physicsEngine, &EntitySimulation::emitEntityCollisionWithEntity, + connect(&_physicsEngine, &EntitySimulation::entityCollisionWithEntity, &_entities, &EntityTreeRenderer::entityCollisionWithEntity); // connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 12d4e9e61e..6d27e7d0cc 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -188,7 +188,7 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { EntityItemID idA = entityA->getEntityItemID(); EntityItemID idB = entityB->getEntityItemID(); - emitEntityCollisionWithEntity(idA, idB, collision); + emit entityCollisionWithEntity(idA, idB, collision); } } } diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index a7e18b67df..e7f2219c19 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -64,7 +64,7 @@ public: EntityTree* getEntityTree() { return _entityTree; } signals: - void emitEntityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); + void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); protected: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index dea321be22..02304b3b8d 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -281,11 +281,11 @@ void PhysicsEngine::computeCollisionEvents() { if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) { idB = static_cast(B)->getEntity()->getEntityItemID(); } - emitEntityCollisionWithEntity(idA, idB, contactItr->second); + emit entityCollisionWithEntity(idA, idB, contactItr->second); } else if (B && B->getType() == MOTION_STATE_TYPE_ENTITY) { EntityItemID idA; EntityItemID idB = static_cast(B)->getEntity()->getEntityItemID(); - emitEntityCollisionWithEntity(idA, idB, contactItr->second); + emit entityCollisionWithEntity(idA, idB, contactItr->second); } // TODO: enable scripts to filter based on contact event type From 1b7074e52a0278e8a3a06b8165f8fe91e5712636 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 21 Jan 2015 14:16:34 -0800 Subject: [PATCH 16/59] purge EntityCollisionSystem --- interface/src/Application.h | 1 - .../entities/src/EntityCollisionSystem.cpp | 297 ------------------ .../entities/src/EntityCollisionSystem.h | 58 ---- 3 files changed, 356 deletions(-) delete mode 100644 libraries/entities/src/EntityCollisionSystem.cpp delete mode 100644 libraries/entities/src/EntityCollisionSystem.h diff --git a/interface/src/Application.h b/interface/src/Application.h index c79cf8a989..3b793978a8 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -25,7 +25,6 @@ #include #include -#include #include #include #include diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp deleted file mode 100644 index 6d27e7d0cc..0000000000 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ /dev/null @@ -1,297 +0,0 @@ -// -// 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 -#include - -#include "EntityCollisionSystem.h" -#include "EntityEditPacketSender.h" -#include "EntityItem.h" -#include "EntityTreeElement.h" -#include "EntityTree.h" - -const int MAX_COLLISIONS_PER_Entity = 16; - -EntityCollisionSystem::EntityCollisionSystem() - : SimpleEntitySimulation(), - _packetSender(NULL), - _avatars(NULL), - _collisions(MAX_COLLISIONS_PER_Entity) { -} - -void EntityCollisionSystem::init(EntityEditPacketSender* packetSender, - EntityTree* entities, AvatarHashMap* avatars) { - assert(entities); - setEntityTree(entities); - _packetSender = packetSender; - _avatars = avatars; -} - -EntityCollisionSystem::~EntityCollisionSystem() { -} - -void EntityCollisionSystem::updateCollisions() { - PerformanceTimer perfTimer("collisions"); - assert(_entityTree); - // update all Entities - if (_entityTree->tryLockForWrite()) { - foreach (EntityItem* entity, _movingEntities) { - checkEntity(entity); - } - _entityTree->unlock(); - } -} - - -void EntityCollisionSystem::checkEntity(EntityItem* entity) { - updateCollisionWithEntities(entity); - updateCollisionWithAvatars(entity); -} - -void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { - - if (entityA->getIgnoreForCollisions()) { - return; // bail early if this entity is to be ignored... - } - - // don't collide entities with unknown IDs, - if (!entityA->isKnownID()) { - return; - } - - glm::vec3 penetration; - EntityItem* entityB = NULL; - - const int MAX_COLLISIONS_PER_ENTITY = 32; - CollisionList collisions(MAX_COLLISIONS_PER_ENTITY); - bool shapeCollisionsAccurate = false; - - bool shapeCollisions = _entityTree->findShapeCollisions(&entityA->getCollisionShapeInMeters(), - collisions, Octree::NoLock, &shapeCollisionsAccurate); - - if (shapeCollisions) { - for(int i = 0; i < collisions.size(); i++) { - - CollisionInfo* collision = collisions[i]; - penetration = collision->_penetration; - entityB = static_cast(collision->_extraData); - - // The collision _extraData should be a valid entity, but if for some reason - // it's NULL then continue with a warning. - if (!entityB) { - qDebug() << "UNEXPECTED - we have a collision with missing _extraData. Something went wrong down below!"; - continue; // skip this loop pass if the entity is NULL - } - - // don't collide entities with unknown IDs, - if (!entityB->isKnownID()) { - continue; // skip this loop pass if the entity has an unknown ID - } - - // NOTE: 'penetration' is the depth that 'entityA' overlaps 'entityB'. It points from A into B. - glm::vec3 penetrationInTreeUnits = penetration / (float)(TREE_SCALE); - - // 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(); - - bool fullyEnclosedCollision = glm::length(penetrationInTreeUnits) > entityA->getLargestDimension(); - - bool wantToMoveA = entityA->getCollisionsWillMove(); - bool wantToMoveB = entityB->getCollisionsWillMove(); - bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f; - - // only do collisions if the entities are moving toward each other and one or the other - // of the entities are movable from collisions - bool doCollisions = !fullyEnclosedCollision && movingTowardEachOther && (wantToMoveA || wantToMoveB); - - if (doCollisions) { - - quint64 now = usecTimestampNow(); - - glm::vec3 axis = glm::normalize(penetration); - glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; - - float massA = entityA->computeMass(); - float massB = entityB->computeMass(); - float totalMass = massA + massB; - float massRatioA = (2.0f * massB / totalMass); - float massRatioB = (2.0f * massA / totalMass); - - // in the event that one of our entities is non-moving, then fix up these ratios - if (wantToMoveA && !wantToMoveB) { - massRatioA = 2.0f; - massRatioB = 0.0f; - } - - if (!wantToMoveA && wantToMoveB) { - massRatioA = 0.0f; - massRatioB = 2.0f; - } - - // unless the entity is configured to not be moved by collision, calculate it's new position - // and velocity and apply it - if (wantToMoveA) { - // handle Entity A - glm::vec3 newVelocityA = entityA->getVelocity() - axialVelocity * massRatioA; - glm::vec3 newPositionA = entityA->getPosition() - 0.5f * penetrationInTreeUnits; - - EntityItemProperties propertiesA = entityA->getProperties(); - EntityItemID idA(entityA->getID()); - propertiesA.setVelocity(newVelocityA * (float)TREE_SCALE); - propertiesA.setPosition(newPositionA * (float)TREE_SCALE); - propertiesA.setLastEdited(now); - - // NOTE: EntityTree::updateEntity() will cause the entity to get sorted correctly in the EntitySimulation, - // thereby waking up static non-moving entities. - _entityTree->updateEntity(entityA, propertiesA); - _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); - } - - // unless the entity is configured to not be moved by collision, calculate it's new position - // and velocity and apply it - if (wantToMoveB) { - glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * massRatioB; - glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits; - - EntityItemProperties propertiesB = entityB->getProperties(); - - EntityItemID idB(entityB->getID()); - propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE); - propertiesB.setPosition(newPositionB * (float)TREE_SCALE); - propertiesB.setLastEdited(now); - - // NOTE: EntityTree::updateEntity() will cause the entity to get sorted correctly in the EntitySimulation, - // thereby waking up static non-moving entities. - _entityTree->updateEntity(entityB, propertiesB); - _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); - } - - // NOTE: Do this after updating the entities so that the callback can delete the entities if they want to - Collision collision; - collision.penetration = penetration; - collision.contactPoint = (0.5f * (float)TREE_SCALE) * (entityA->getPosition() + entityB->getPosition()); - - EntityItemID idA = entityA->getEntityItemID(); - EntityItemID idB = entityB->getEntityItemID(); - emit entityCollisionWithEntity(idA, idB, collision); - } - } - } -} - -void EntityCollisionSystem::updateCollisionWithAvatars(EntityItem* entity) { - - // Entities that are in hand, don't collide with avatars - if (!_avatars) { - return; - } - - if (entity->getIgnoreForCollisions() || !entity->getCollisionsWillMove()) { - return; // bail early if this entity is to be ignored or wont move - } - - 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; - 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.0f) { - // only collide when Entity and collision point are moving toward each other - // (doing this prevents some "collision snagging" when Entity penetrates the object) - collision->_penetration /= (float)(TREE_SCALE); - applyHardCollision(entity, *collision); - } - } - } - } -} - -void EntityCollisionSystem::applyHardCollision(EntityItem* entity, const CollisionInfo& collisionInfo) { - - // don't collide entities with unknown IDs, - if (!entity->isKnownID()) { - return; - } - - // 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 = entity->getPosition(); - glm::vec3 velocity = entity->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 - } - } - - EntityItemProperties properties = entity->getProperties(); - EntityItemID entityItemID(entity->getID()); - - properties.setPosition(position * (float)TREE_SCALE); - properties.setVelocity(velocity * (float)TREE_SCALE); - properties.setLastEdited(usecTimestampNow()); - - _entityTree->updateEntity(entity, properties); - _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, entityItemID, properties); -} diff --git a/libraries/entities/src/EntityCollisionSystem.h b/libraries/entities/src/EntityCollisionSystem.h deleted file mode 100644 index d6cafd88f3..0000000000 --- a/libraries/entities/src/EntityCollisionSystem.h +++ /dev/null @@ -1,58 +0,0 @@ -// -// 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 "EntityItem.h" -#include "SimpleEntitySimulation.h" - -class AbstractAudioInterface; -class AvatarData; -class EntityEditPacketSender; -class EntityTree; - -class EntityCollisionSystem : public SimpleEntitySimulation { -public: - EntityCollisionSystem(); - - void init(EntityEditPacketSender* packetSender, EntityTree* entities, AvatarHashMap* _avatars = NULL); - - ~EntityCollisionSystem(); - - void updateCollisions(); - - void checkEntity(EntityItem* Entity); - void updateCollisionWithEntities(EntityItem* Entity); - void updateCollisionWithAvatars(EntityItem* Entity); - -private: - void applyHardCollision(EntityItem* entity, const CollisionInfo& collisionInfo); - - static bool updateOperation(OctreeElement* element, void* extraData); - - EntityEditPacketSender* _packetSender; - AvatarHashMap* _avatars; - CollisionList _collisions; -}; - -#endif // hifi_EntityCollisionSystem_h From 53a70c43e1b6f2cbd4d99cc522945749b6f2f6d1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Jan 2015 15:08:38 -0800 Subject: [PATCH 17/59] Add width and height to NetworkTexture --- libraries/render-utils/src/TextureCache.cpp | 6 +++++- libraries/render-utils/src/TextureCache.h | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index 3bd05a14ee..4c9abc74a1 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -385,7 +385,9 @@ Texture::~Texture() { NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) : Resource(url, !content.isEmpty()), _type(type), - _translucent(false) { + _translucent(false), + _width(0), + _height(0) { if (!url.isValid()) { _loaded = true; @@ -532,6 +534,8 @@ void NetworkTexture::loadContent(const QByteArray& content) { void NetworkTexture::setImage(const QImage& image, bool translucent, const QColor& averageColor) { _translucent = translucent; _averageColor = averageColor; + _width = image.width(); + _height = image.height(); finishedLoading(true); imageLoaded(image); diff --git a/libraries/render-utils/src/TextureCache.h b/libraries/render-utils/src/TextureCache.h index 54c98a61cb..efcccc4b8c 100644 --- a/libraries/render-utils/src/TextureCache.h +++ b/libraries/render-utils/src/TextureCache.h @@ -150,6 +150,9 @@ public: /// Returns the lazily-computed average texture color. const QColor& getAverageColor() const { return _averageColor; } + int getWidth() const { return _width; } + int getHeight() const { return _height; } + protected: virtual void downloadFinished(QNetworkReply* reply); @@ -163,6 +166,8 @@ private: TextureType _type; bool _translucent; QColor _averageColor; + int _width; + int _height; }; /// Caches derived, dilated textures. From ba2f7d88ce3cdd7c8ad372457fc5cf9c7c709345 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Jan 2015 15:09:09 -0800 Subject: [PATCH 18/59] Update ImageOverlay to use TextureCache --- interface/src/ui/overlays/ImageOverlay.cpp | 51 ++++++++-------------- interface/src/ui/overlays/ImageOverlay.h | 7 +-- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 1ecfc74e44..164b3916db 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -22,9 +22,7 @@ #include "ImageOverlay.h" ImageOverlay::ImageOverlay() : - _textureID(0), _renderImage(false), - _textureBound(false), _wantClipFromImage(false) { _isLoaded = false; @@ -32,63 +30,48 @@ ImageOverlay::ImageOverlay() : ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) : Overlay2D(imageOverlay), + _texture(imageOverlay->_texture), _imageURL(imageOverlay->_imageURL), _textureImage(imageOverlay->_textureImage), - _textureID(0), - _fromImage(), + _fromImage(imageOverlay->_fromImage), _renderImage(imageOverlay->_renderImage), - _textureBound(false), - _wantClipFromImage(false) + _wantClipFromImage(imageOverlay->_wantClipFromImage) { } ImageOverlay::~ImageOverlay() { - if (_parent && _textureID) { - // do we need to call this? - //_parent->deleteTexture(_textureID); - } } // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { _imageURL = url; _isLoaded = false; - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); - connect(reply, &QNetworkReply::finished, this, &ImageOverlay::replyFinished); -} - -void ImageOverlay::replyFinished() { - QNetworkReply* reply = static_cast(sender()); - - // replace our byte array with the downloaded data - QByteArray rawData = reply->readAll(); - _textureImage.loadFromData(rawData); - _renderImage = true; - _isLoaded = true; - reply->deleteLater(); } void ImageOverlay::render(RenderArgs* args) { - if (!_visible || !_isLoaded) { - return; // do nothing if we're not visible + if (!_isLoaded && !_imageURL.isEmpty()) { + _isLoaded = true; + _renderImage = true; + qDebug() << "Now loding texture for ImageOverlay"; + _texture = DependencyManager::get()->getTexture(_imageURL); } - if (_renderImage && !_textureBound) { - _textureID = _parent->bindTexture(_textureImage); - _textureBound = true; + + if (!_visible || !_isLoaded || !_texture || !_texture->isLoaded()) { + return; } if (_renderImage) { + qDebug() << "Rendering: " << _imageURL << ", " << _texture->getWidth() << ", " << _texture->getHeight(); glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, _textureID); + glBindTexture(GL_TEXTURE_2D, _texture->getID()); } const float MAX_COLOR = 255.0f; xColor color = getColor(); float alpha = getAlpha(); glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - float imageWidth = _textureImage.width(); - float imageHeight = _textureImage.height(); + float imageWidth = _texture->getWidth(); + float imageHeight = _texture->getHeight(); QRect fromImage; if (_wantClipFromImage) { @@ -111,8 +94,8 @@ void ImageOverlay::render(RenderArgs* args) { glm::vec2 topLeft(left, top); glm::vec2 bottomRight(right, bottom); - glm::vec2 texCoordTopLeft(x, 1.0f - y); - glm::vec2 texCoordBottomRight(x + w, 1.0f - (y + h)); + glm::vec2 texCoordTopLeft(x, y); + glm::vec2 texCoordBottomRight(x + w, y + h); if (_renderImage) { DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight); diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index cff557654f..e167c4e755 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -24,6 +24,7 @@ #include #include +#include #include "Overlay.h" #include "Overlay2D.h" @@ -49,18 +50,14 @@ public: virtual ImageOverlay* createClone() const; -private slots: - void replyFinished(); // we actually want to hide this... - private: QUrl _imageURL; QImage _textureImage; - GLuint _textureID; + NetworkTexturePointer _texture; QRect _fromImage; // where from in the image to sample bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle - bool _textureBound; // has the texture been bound bool _wantClipFromImage; }; From 8bd944e32e038738a95690e8af63ae630a0680f5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 Jan 2015 16:10:00 -0800 Subject: [PATCH 19/59] Incremental improvement to stitching. --- interface/src/MetavoxelSystem.cpp | 49 +++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 719326f628..a0d6a8d72e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1427,6 +1427,8 @@ public: void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; } void mix(const EdgeCrossing& first, const EdgeCrossing& second, float t); + + VoxelPoint createPoint(int clampedX, int clampedZ, float step) const; }; void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, float t) { @@ -1437,6 +1439,16 @@ void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, fl material = (t < 0.5f) ? first.material : second.material; } +VoxelPoint EdgeCrossing::createPoint(int clampedX, int clampedZ, float step) const { + VoxelPoint voxelPoint = { glm::vec3(clampedX + point.x, point.y, clampedZ + point.z) * step, + { (quint8)qRed(color), (quint8)qGreen(color), (quint8)qBlue(color) }, + { (char)(normal.x * numeric_limits::max()), (char)(normal.y * numeric_limits::max()), + (char)(normal.z * numeric_limits::max()) }, + { (quint8)material, 0, 0, 0 }, + { numeric_limits::max(), 0, 0, 0 } }; + return voxelPoint; +} + const int MAX_NORMALS_PER_VERTEX = 4; class NormalIndex { @@ -1509,6 +1521,19 @@ static inline glm::vec3 getNormal(const QVector& vertices, const Nor return glm::cross(vertices.at(i2.indices[0]).vertex - v0, vertices.at(i3.indices[0]).vertex - v0); } +static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1, const EdgeCrossing& e2, + int clampedX, int clampedZ, float step, QVector& vertices, QVector& indices, + QMultiHash& quadIndices) { + int firstIndex = vertices.size(); + vertices.append(e0.createPoint(clampedX, clampedZ, step)); + vertices.append(e1.createPoint(clampedX, clampedZ, step)); + vertices.append(e2.createPoint(clampedX, clampedZ, step)); + indices.append(firstIndex); + indices.append(firstIndex + 1); + indices.append(firstIndex + 2); + indices.append(firstIndex + 2); +} + void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { if (!node->getHeight()) { @@ -1865,7 +1890,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g switch (crossedCorners) { case UPPER_LEFT_CORNER: case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: - case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: + case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: case UPPER_LEFT_CORNER | LOWER_RIGHT_CORNER: crossings[crossingCount++] = cornerCrossings[0]; crossings[crossingCount - 1].point.y -= y; @@ -1873,22 +1898,34 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g case UPPER_RIGHT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: - case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: crossings[crossingCount++] = cornerCrossings[1]; crossings[crossingCount - 1].point.y -= y; break; - + + case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: + crossings[crossingCount++] = cornerCrossings[1]; + crossings[crossingCount - 1].point.y -= y; + appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2], + clampedX, clampedZ, step, vertices, indices, quadIndices); + break; + case LOWER_LEFT_CORNER: case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: - case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: crossings[crossingCount++] = cornerCrossings[2]; crossings[crossingCount - 1].point.y -= y; break; - + + case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: + crossings[crossingCount++] = cornerCrossings[2]; + crossings[crossingCount - 1].point.y -= y; + appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1], + clampedX, clampedZ, step, vertices, indices, quadIndices); + break; + case LOWER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: - case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: + case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: crossings[crossingCount++] = cornerCrossings[3]; crossings[crossingCount - 1].point.y -= y; break; From 6567fcfe0b36eb253cd2a8afff31b7eda37d1133 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 21 Jan 2015 16:17:25 -0800 Subject: [PATCH 20/59] Added shootable blocks on platform, grenade, buttons for making grid of targets --- examples/controllers/hydra/gun.js | 214 ++++++++++++++++++------------ 1 file changed, 126 insertions(+), 88 deletions(-) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index d190fff503..0c0740e12b 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -112,6 +112,25 @@ var offButton = Overlays.addOverlay("image", { alpha: 1 }); +var platformButton = Overlays.addOverlay("image", { + x: screenSize.x - 48, + y: 130, + width: 32, + height: 32, + imageURL: HIFI_PUBLIC_BUCKET + "images/city.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); +var gridButton = Overlays.addOverlay("image", { + x: screenSize.x - 48, + y: 164, + width: 32, + height: 32, + imageURL: HIFI_PUBLIC_BUCKET + "images/blocks.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + if (showScore) { var text = Overlays.addOverlay("text", { x: screenSize.x / 2 - 100, @@ -126,26 +145,30 @@ if (showScore) { }); } -function printVector(string, vector) { - print(string + " " + vector.x + ", " + vector.y + ", " + vector.z); -} +var BULLET_VELOCITY = 10.0; -var BULLET_VELOCITY = 15.0; - -function shootBullet(position, velocity) { - var BULLET_SIZE = 0.07; +function shootBullet(position, velocity, grenade) { + var BULLET_SIZE = 0.10; var BULLET_LIFETIME = 10.0; var BULLET_GRAVITY = -0.25; + var GRENADE_VELOCITY = 15.0; + var GRENADE_SIZE = 0.35; + var GRENADE_GRAVITY = -9.8; + + var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity; + var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE; + var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; + bulletID = Entities.addEntity( { type: "Sphere", position: position, - dimensions: { x: BULLET_SIZE, y: BULLET_SIZE, z: BULLET_SIZE }, + dimensions: { x: bSize, y: bSize, z: bSize }, color: { red: 255, green: 0, blue: 0 }, - velocity: velocity, + velocity: bVelocity, lifetime: BULLET_LIFETIME, - gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, + gravity: { x: 0, y: bGravity, z: 0 }, damping: 0.01, - density: 10000, + density: 8000, ignoreCollisions: false, collisionsWillMove: true }); @@ -168,51 +191,6 @@ function shootBullet(position, velocity) { MyAvatar.setJointData("LeftForeArm", armRotation); elbowKickAngle = KICKBACK_ANGLE; } - -function makeGrid(type, gravity, scale, size) { - var separation = scale * 2; - var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); - pos.y -= separation * size; - var x, y, z; - var GRID_LIFE = 60.0; - var dimensions; - - for (x = 0; x < size; x++) { - for (y = 0; y < size; y++) { - for (z = 0; z < size; z++) { - if (gravity == 0) { - dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 }; - } else { - dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/4.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) }; - } - - Entities.addEntity( - { type: type, - position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation }, - dimensions: dimensions, - color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, - velocity: { x: 0, y: 0, z: 0 }, - gravity: { x: 0, y: gravity, z: 0 }, - lifetime: GRID_LIFE, - rotation: Camera.getOrientation(), - damping: 0.1, - density: 100.0, - collisionsWillMove: true }); - } - } - } - if (gravity < 0) { - // Make a floor for this stuff to fall onto - Entities.addEntity({ - type: "Box", - position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, - dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size }, - color: { red: 128, green: 128, blue: 128 }, - lifetime: GRID_LIFE - }); - } -} - function shootTarget() { var TARGET_SIZE = 0.50; var TARGET_GRAVITY = 0.0; @@ -222,7 +200,7 @@ function shootTarget() { var DISTANCE_TO_LAUNCH_FROM = 5.0; var ANGLE_RANGE_FOR_LAUNCH = 20.0; var camera = Camera.getPosition(); - //printVector("camera", camera); + var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 }); targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); var forwardVector = Quat.getFront(targetDirection); @@ -253,6 +231,78 @@ function shootTarget() { Audio.playSound(targetLaunchSound, audioOptions); } +function makeGrid(type, scale, size) { + var separation = scale * 2; + var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); + var x, y, z; + var GRID_LIFE = 60.0; + var dimensions; + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + for (z = 0; z < size; z++) { + + dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 }; + + Entities.addEntity( + { type: type, + position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation }, + dimensions: dimensions, + color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + lifetime: GRID_LIFE, + rotation: Camera.getOrientation(), + damping: 0.1, + density: 100.0, + collisionsWillMove: true }); + } + } + } +} +function makePlatform(gravity, scale, size) { + var separation = scale * 2; + var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); + pos.y -= separation * size; + var x, y, z; + var TARGET_LIFE = 60.0; + var INITIAL_GAP = 0.5; + var dimensions; + + for (x = 0; x < size; x++) { + for (y = 0; y < size; y++) { + for (z = 0; z < size; z++) { + + dimensions = { x: separation/2.0, y: separation, z: separation/2.0 }; + + Entities.addEntity( + { type: "Box", + position: { x: pos.x - (separation * size / 2.0) + x * separation, + y: pos.y + y * (separation + INITIAL_GAP), + z: pos.z - (separation * size / 2.0) + z * separation }, + dimensions: dimensions, + color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: gravity, z: 0 }, + lifetime: TARGET_LIFE, + damping: 0.1, + density: 100.0, + collisionsWillMove: true }); + } + } + } + + // Make a floor for this stuff to fall onto + Entities.addEntity({ + type: "Box", + position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, + dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size }, + color: { red: 128, green: 128, blue: 128 }, + lifetime: TARGET_LIFE + }); + +} + function entityCollisionWithEntity(entity1, entity2, collision) { if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) && @@ -281,19 +331,16 @@ function keyPressEvent(event) { var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; Script.setTimeout(shootTarget, time); } else if ((event.text == ".") || (event.text == "SPACE")) { - shootFromMouse(); + shootFromMouse(false); + } else if (event.text == ",") { + shootFromMouse(true); } else if (event.text == "r") { playLoadSound(); - } else if (event.text == "g") { - makeGrid("Box", 0.0, 0.4, 3); - } else if (event.text == "f") { - makeGrid("Box", -9.8, 0.4, 5); } else if (event.text == "s") { // Hit this key to dump a posture from hydra to log Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm")); Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm")); Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand")); - } } @@ -339,18 +386,7 @@ function update(deltaTime) { if (targetID && !targetID.isKnownID) { targetID = Entities.identifyEntity(targetID); } - // Check for mouseLook movement, update rotation - // rotate body yaw for yaw received from mouse - var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } )); - //MyAvatar.orientation = newOrientation; - yawFromMouse = 0; - // apply pitch from mouse - var newPitch = MyAvatar.headPitch + pitchFromMouse; - //MyAvatar.headPitch = newPitch; - pitchFromMouse = 0; - - if (activeControllers == 0) { if (Controller.getNumberOfSpatialControls() > 0) { activeControllers = Controller.getNumberOfSpatialControls(); @@ -424,19 +460,19 @@ function update(deltaTime) { var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector)); - shootBullet(position, velocity); + shootBullet(position, velocity, false); } } } } -function shootFromMouse() { +function shootFromMouse(grenade) { var DISTANCE_FROM_CAMERA = 1.0; var camera = Camera.getPosition(); var forwardVector = Quat.getFront(Camera.getOrientation()); var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); - shootBullet(newPosition, velocity); + shootBullet(newPosition, velocity, grenade); } function mouseReleaseEvent(event) { @@ -444,21 +480,23 @@ function mouseReleaseEvent(event) { isMouseDown = false; } -function mouseMoveEvent(event) { - if (isMouseDown) { - var MOUSE_YAW_SCALE = -0.25; - var MOUSE_PITCH_SCALE = -12.5; - var FIXED_MOUSE_TIMESTEP = 0.016; - yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP); - pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); - lastX = event.x; - lastY = event.y; - } +function mousePressEvent(event) { + var clickedText = false; + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + if (clickedOverlay == offButton) { + Script.stop(); + } else if (clickedOverlay == platformButton) { + makePlatform(-9.8, 1.0, 5); + } else if (clickedOverlay == gridButton) { + makeGrid("Box", 1.0, 3); + } } function scriptEnding() { Overlays.deleteOverlay(reticle); Overlays.deleteOverlay(offButton); + Overlays.deleteOverlay(platformButton); + Overlays.deleteOverlay(gridButton); Overlays.deleteOverlay(pointer[0]); Overlays.deleteOverlay(pointer[1]); Overlays.deleteOverlay(text); @@ -470,7 +508,7 @@ Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity); Script.scriptEnding.connect(scriptEnding); Script.update.connect(update); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); -Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mousePressEvent.connect(mousePressEvent); Controller.keyPressEvent.connect(keyPressEvent); From 0d8380a28839d846c5146e9c76f404802d4114c5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 Jan 2015 17:47:13 -0800 Subject: [PATCH 21/59] Put the indices for our extra triangles into the spatial hash. --- interface/src/MetavoxelSystem.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index a0d6a8d72e..2161887b91 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1532,6 +1532,12 @@ static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1 indices.append(firstIndex + 1); indices.append(firstIndex + 2); indices.append(firstIndex + 2); + + int minimumY = qMin((int)e0.point.y, qMin((int)e1.point.y, (int)e2.point.y)); + int maximumY = qMax((int)e0.point.y, qMax((int)e1.point.y, (int)e2.point.y)); + for (int y = minimumY; y <= maximumY; y++) { + quadIndices.insert(qRgb(clampedX, y, clampedZ), firstIndex); + } } void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, @@ -1895,7 +1901,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossings[crossingCount++] = cornerCrossings[0]; crossings[crossingCount - 1].point.y -= y; break; - + case UPPER_RIGHT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: @@ -1909,7 +1915,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2], clampedX, clampedZ, step, vertices, indices, quadIndices); break; - + case LOWER_LEFT_CORNER: case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: crossings[crossingCount++] = cornerCrossings[2]; @@ -1922,7 +1928,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1], clampedX, clampedZ, step, vertices, indices, quadIndices); break; - + case LOWER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: From 2751fab2c360892ac1b629de6d6b68d46b7c0abd Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 21 Jan 2015 18:02:13 -0800 Subject: [PATCH 22/59] don't send collision events for inactive pairs also throttle collision events to 30/sec --- libraries/physics/src/PhysicsEngine.cpp | 30 ++++++++++++++++++++++--- libraries/physics/src/PhysicsEngine.h | 1 + 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 02304b3b8d..74b792e3c0 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -249,23 +249,47 @@ void PhysicsEngine::stepSimulation() { } void PhysicsEngine::computeCollisionEvents() { - // update all contacts + // update all contacts every frame int numManifolds = _collisionDispatcher->getNumManifolds(); for (int i = 0; i < numManifolds; ++i) { btPersistentManifold* contactManifold = _collisionDispatcher->getManifoldByIndexInternal(i); if (contactManifold->getNumContacts() > 0) { + // TODO: require scripts to register interest in callbacks for specific objects + // so we can filter out most collision events right here. const btCollisionObject* objectA = static_cast(contactManifold->getBody0()); const btCollisionObject* objectB = static_cast(contactManifold->getBody1()); + + if (!(objectA->isActive() || objectB->isActive())) { + // both objects are inactive so stop tracking this contact, + // which will eventually trigger a CONTACT_EVENT_TYPE_END + continue; + } void* a = objectA->getUserPointer(); void* b = objectB->getUserPointer(); if (a || b) { // the manifold has up to 4 distinct points, but only extract info from the first - _contactMap[ContactKey(a, b)].update(_numSubsteps, contactManifold->getContactPoint(0), _originOffset); + _contactMap[ContactKey(a, b)].update(_numContactFrames, contactManifold->getContactPoint(0), _originOffset); } } } + // We harvest collision callbacks every few frames, which contributes the following effects: + // + // (1) There is a maximum collision callback rate per pair: substep_rate / SUBSTEPS_PER_COLLIION_FRAME + // (2) END/START cycles shorter than SUBSTEPS_PER_COLLIION_FRAME will be filtered out + // (3) There is variable lag between when the contact actually starts and when it is reported, + // up to SUBSTEPS_PER_COLLIION_FRAME * time_per_substep + // + const uint32_t SUBSTEPS_PER_COLLISION_FRAME = 2; + if (_numSubsteps - _numContactFrames * SUBSTEPS_PER_COLLISION_FRAME < SUBSTEPS_PER_COLLISION_FRAME) { + // we don't harvest collision callbacks every frame + // this sets a maximum callback-per-contact rate + // and also filters out END/START events that happen on shorter timescales + return; + } + + ++_numContactFrames; // scan known contacts and trigger events ContactMap::iterator contactItr = _contactMap.begin(); while (contactItr != _contactMap.end()) { @@ -289,7 +313,7 @@ void PhysicsEngine::computeCollisionEvents() { } // TODO: enable scripts to filter based on contact event type - ContactEventType type = contactItr->second.computeType(_numSubsteps); + ContactEventType type = contactItr->second.computeType(_numContactFrames); if (type == CONTACT_EVENT_TYPE_END) { ContactMap::iterator iterToDelete = contactItr; ++contactItr; diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 73a02607e8..d333eef010 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -110,6 +110,7 @@ private: EntityEditPacketSender* _entityPacketSender = NULL; ContactMap _contactMap; + uint32_t _numContactFrames = 0; }; #endif // hifi_PhysicsEngine_h From b2e79a0157ad5786314783acf1ea5be8e0804583 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 21 Jan 2015 18:06:19 -0800 Subject: [PATCH 23/59] don't process results when no simulation --- libraries/physics/src/PhysicsEngine.cpp | 32 +++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 74b792e3c0..55920cf315 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -231,21 +231,23 @@ void PhysicsEngine::stepSimulation() { _numSubsteps += (uint32_t)numSubsteps; unlock(); - // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. - // - // Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree - // to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this - // PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own - // lock on the tree before we re-lock ourselves. - // - // TODO: untangle these lock sequences. - _entityTree->lockForWrite(); - lock(); - _dynamicsWorld->synchronizeMotionStates(); - unlock(); - _entityTree->unlock(); - - computeCollisionEvents(); + if (_numSubsteps > 0) { + // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. + // + // Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree + // to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this + // PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own + // lock on the tree before we re-lock ourselves. + // + // TODO: untangle these lock sequences. + _entityTree->lockForWrite(); + lock(); + _dynamicsWorld->synchronizeMotionStates(); + unlock(); + _entityTree->unlock(); + + computeCollisionEvents(); + } } void PhysicsEngine::computeCollisionEvents() { From 77c436299135daac4e28a479305cfc012b3928ec Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 21 Jan 2015 18:14:24 -0800 Subject: [PATCH 24/59] fix typo --- libraries/physics/src/PhysicsEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 55920cf315..7f2b139058 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -231,7 +231,7 @@ void PhysicsEngine::stepSimulation() { _numSubsteps += (uint32_t)numSubsteps; unlock(); - if (_numSubsteps > 0) { + if (numSubsteps > 0) { // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. // // Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree From 52193fce387e1d4511838d4d5691de25e8c62771 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 08:35:38 -0800 Subject: [PATCH 25/59] Remove qDebug --- interface/src/ui/overlays/ImageOverlay.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 164b3916db..b5c235d828 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -52,7 +52,6 @@ void ImageOverlay::render(RenderArgs* args) { if (!_isLoaded && !_imageURL.isEmpty()) { _isLoaded = true; _renderImage = true; - qDebug() << "Now loding texture for ImageOverlay"; _texture = DependencyManager::get()->getTexture(_imageURL); } From 0e9e77f1667c15a2ab032ed362c00c5a54e46914 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 08:58:55 -0800 Subject: [PATCH 26/59] Fix broken support for color-only ImageOverlay --- interface/src/ui/overlays/ImageOverlay.cpp | 63 +++++++++++++--------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index b5c235d828..e18f99072f 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -22,10 +22,10 @@ #include "ImageOverlay.h" ImageOverlay::ImageOverlay() : + _imageURL(), _renderImage(false), _wantClipFromImage(false) { - _isLoaded = false; } ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) : @@ -45,47 +45,39 @@ ImageOverlay::~ImageOverlay() { // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { _imageURL = url; - _isLoaded = false; + + if (url.isEmpty()) { + _isLoaded = true; + _renderImage = false; + _texture.clear(); + } else { + _isLoaded = false; + _renderImage = true; + } } void ImageOverlay::render(RenderArgs* args) { - if (!_isLoaded && !_imageURL.isEmpty()) { + if (!_isLoaded && _renderImage) { _isLoaded = true; - _renderImage = true; _texture = DependencyManager::get()->getTexture(_imageURL); } - if (!_visible || !_isLoaded || !_texture || !_texture->isLoaded()) { + // If we are not visible or loaded, return. If we are trying to render an + // image but the texture hasn't loaded, return. + if (!_visible || !_isLoaded || (_renderImage && !_texture->isLoaded())) { return; } if (_renderImage) { - qDebug() << "Rendering: " << _imageURL << ", " << _texture->getWidth() << ", " << _texture->getHeight(); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, _texture->getID()); } + const float MAX_COLOR = 255.0f; xColor color = getColor(); float alpha = getAlpha(); glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - float imageWidth = _texture->getWidth(); - float imageHeight = _texture->getHeight(); - - QRect fromImage; - if (_wantClipFromImage) { - fromImage = _fromImage; - } else { - fromImage.setX(0); - fromImage.setY(0); - fromImage.setWidth(imageWidth); - fromImage.setHeight(imageHeight); - } - float x = fromImage.x() / imageWidth; - float y = fromImage.y() / imageHeight; - float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure - float h = fromImage.height() / imageHeight; - int left = _bounds.left(); int right = _bounds.right() + 1; int top = _bounds.top(); @@ -93,10 +85,29 @@ void ImageOverlay::render(RenderArgs* args) { glm::vec2 topLeft(left, top); glm::vec2 bottomRight(right, bottom); - glm::vec2 texCoordTopLeft(x, y); - glm::vec2 texCoordBottomRight(x + w, y + h); if (_renderImage) { + float imageWidth = _texture->getWidth(); + float imageHeight = _texture->getHeight(); + + QRect fromImage; + if (_wantClipFromImage) { + fromImage = _fromImage; + } else { + fromImage.setX(0); + fromImage.setY(0); + fromImage.setWidth(imageWidth); + fromImage.setHeight(imageHeight); + } + + float x = fromImage.x() / imageWidth; + float y = fromImage.y() / imageHeight; + float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure + float h = fromImage.height() / imageHeight; + + glm::vec2 texCoordTopLeft(x, y); + glm::vec2 texCoordBottomRight(x + w, y + h); + DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight); } else { DependencyManager::get()->renderQuad(topLeft, bottomRight); @@ -135,7 +146,7 @@ void ImageOverlay::setProperties(const QScriptValue& properties) { subImageRect.setHeight(oldSubImageRect.height()); } setClipFromSource(subImageRect); - } + } QScriptValue imageURL = properties.property("imageURL"); if (imageURL.isValid()) { From 3997b916c1bcccb640f6f2ce8b11cc4426feef4a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Jan 2015 09:17:51 -0800 Subject: [PATCH 27/59] remove ground collision hack for physics testing --- libraries/physics/src/PhysicsEngine.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 7f2b139058..c374f03705 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -186,22 +186,6 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) { // default gravity of the world is zero, so each object must specify its own gravity // TODO: set up gravity zones _dynamicsWorld->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - - // GROUND HACK: add a big planar floor (and walls for testing) to catch falling objects - btTransform groundTransform; - groundTransform.setIdentity(); - for (int i = 0; i < 3; ++i) { - btVector3 normal(0.0f, 0.0f, 0.0f); - normal[i] = 1.0f; - btCollisionShape* plane = new btStaticPlaneShape(normal, 0.0f); - - btCollisionObject* groundObject = new btCollisionObject(); - groundObject->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); - groundObject->setCollisionShape(plane); - - groundObject->setWorldTransform(groundTransform); - _dynamicsWorld->addCollisionObject(groundObject); - } } assert(packetSender); From 45a84895e10a0ef649ddcdaed5297356a2e93b96 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 22 Jan 2015 12:44:07 -0800 Subject: [PATCH 28/59] Paddle ball game, first version --- examples/controllers/hydra/paddleBall.js | 164 +++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 examples/controllers/hydra/paddleBall.js diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js new file mode 100644 index 0000000000..e5d409bd8a --- /dev/null +++ b/examples/controllers/hydra/paddleBall.js @@ -0,0 +1,164 @@ +// PaddleBall.js +// +// Created by Philip Rosedale on January 21, 2015 +// Copyright 2014 High Fidelity, Inc. +// +// Move your hand with the hydra controller, and hit the ball with the paddle. +// Click 'X' button to turn off this script. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var BALL_SIZE = 0.08; +var PADDLE_SIZE = 0.20; +var PADDLE_THICKNESS = 0.06; +var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; +var BALL_COLOR = { red: 255, green: 0, blue: 0 }; +var LINE_COLOR = { red: 255, green: 255, blue: 0 }; +var PADDLE_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; +var GRAVITY = 0.0; +var SPRING_FORCE = 15.0; +var lastSoundTime = 0; +var controllerID = 1; +var gameOn = false; + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"); + +var screenSize = Controller.getViewportDimensions(); +var offButton = Overlays.addOverlay("image", { + x: screenSize.x - 48, + y: 96, + width: 32, + height: 32, + imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + +var ball, paddle, paddleModel, line; + +function createEntities() { + ball = Entities.addEntity( + { type: "Sphere", + position: Controller.getSpatialControlPosition(controllerID), + dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, + color: BALL_COLOR, + gravity: { x: 0, y: GRAVITY, z: 0 }, + ignoreCollisions: false, + damping: 0.50, + collisionsWillMove: true }); + + paddle = Entities.addEntity( + { type: "Box", + position: Controller.getSpatialControlPosition(controllerID), + dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, + color: PADDLE_COLOR, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + damping: 0.10, + visible: false, + rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), + collisionsWillMove: false }); + + modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; + paddleModel = Entities.addEntity( + { type: "Model", + position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_OFFSET), + dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, + color: PADDLE_COLOR, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: true, + modelURL: modelURL, + damping: 0.10, + rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), + collisionsWillMove: false }); + + line = Overlays.addOverlay("line3d", { + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: LINE_COLOR, + alpha: 1, + visible: true, + lineWidth: 2 }); +} + +function deleteEntities() { + Entities.deleteEntity(ball); + Entities.deleteEntity(paddle); + Entities.deleteEntity(paddleModel); + Overlays.deleteOverlay(line); +} + +function update(deltaTime) { + var palmPosition = Controller.getSpatialControlPosition(controllerID); + var controllerActive = (Vec3.length(palmPosition) > 0); + + if (!gameOn && controllerActive) { + createEntities(); + gameOn = true; + } else if (gameOn && !controllerActive) { + deleteEntities(); + gameOn = false; + } + if (!gameOn || !controllerActive) { + return; + } + + if (!paddle.isKnownID) { + paddle = Entities.identifyEntity(paddle); + } + if (!ball.isKnownID) { + ball = Entities.identifyEntity(ball); + } else { + var props = Entities.getEntityProperties(ball); + var spring = Vec3.subtract(palmPosition, props.position); + var paddleWorldOrientation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)); + var springLength = Vec3.length(spring); + spring = Vec3.normalize(spring); + var ballVelocity = Vec3.sum(props.velocity, Vec3.multiply(springLength * SPRING_FORCE * deltaTime, spring)); + Entities.editEntity(ball, { velocity: ballVelocity }); + Overlays.editOverlay(line, { start: props.position, end: palmPosition }); + Entities.editEntity(paddle, { position: palmPosition, + velocity: Controller.getSpatialControlVelocity(controllerID), + rotation: paddleWorldOrientation }); + Entities.editEntity(paddleModel, { position: Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_OFFSET)), + velocity: Controller.getSpatialControlVelocity(controllerID), + rotation: paddleWorldOrientation }); + } +} + +function entityCollisionWithEntity(entity1, entity2, collision) { + if ((entity1.id == ball.id) || (entity2.id ==ball.id)) { + var props1 = Entities.getEntityProperties(entity1); + var props2 = Entities.getEntityProperties(entity2); + var dVel = Vec3.length(Vec3.subtract(props1.velocity, props2.velocity)); + var currentTime = new Date().getTime(); + var MIN_MSECS_BETWEEN_BOUNCE_SOUNDS = 100; + var MIN_VELOCITY_FOR_SOUND_IMPACT = 0.25; + if ((dVel > MIN_VELOCITY_FOR_SOUND_IMPACT) && (currentTime - lastSoundTime) > MIN_MSECS_BETWEEN_BOUNCE_SOUNDS) { + Audio.playSound(hitSound, { position: props1.position, volume: Math.min(dVel, 1.0) }); + lastSoundTime = new Date().getTime(); + } + } +} + +function mousePressEvent(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + if (clickedOverlay == offButton) { + Script.stop(); + } +} + +function scriptEnding() { + if (gameOn) { + deleteEntities(); + } + Overlays.deleteOverlay(offButton); +} + +Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity); +Controller.mousePressEvent.connect(mousePressEvent); +Script.scriptEnding.connect(scriptEnding); +Script.update.connect(update); From 5efc046316d658bca622c585c7983b839734076f Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 22 Jan 2015 15:03:13 -0600 Subject: [PATCH 29/59] Adding http monitoring for ICE server --- ice-server/CMakeLists.txt | 16 +++++++++++++++- ice-server/src/IceServer.cpp | 36 +++++++++++++++++++++++++++++++++++- ice-server/src/IceServer.h | 14 ++++++++++---- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt index 24e780f9aa..a7b2a206e4 100644 --- a/ice-server/CMakeLists.txt +++ b/ice-server/CMakeLists.txt @@ -4,6 +4,20 @@ set(TARGET_NAME ice-server) setup_hifi_project(Network) # link the shared hifi libraries -link_hifi_libraries(networking shared) +link_hifi_libraries(embedded-webserver networking shared) + +# find OpenSSL +find_package(OpenSSL REQUIRED) + +if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") +# this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto +message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." +"\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") +endif () + +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + +# append OpenSSL to our list of libraries to link +target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) include_dependency_includes() \ No newline at end of file diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index c06bb0fc88..531dd4ea22 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -20,14 +20,19 @@ const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000; const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000; +const quint16 ICE_SERVER_MONITORING_PORT = 40110; + IceServer::IceServer(int argc, char* argv[]) : QCoreApplication(argc, argv), _id(QUuid::createUuid()), _serverSocket(), - _activePeers() + _activePeers(), + _httpManager(ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this), + _httpsManager(NULL) { // start the ice-server socket qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT; + qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT; _serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT); // call our process datagrams slot when the UDP socket has packets ready @@ -165,3 +170,32 @@ void IceServer::clearInactivePeers() { } } } + +bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { + // + // We need an HTTP handler in order to monitor the health of the ice server + // The correct functioning of the ICE server will first be determined by its HTTP availability, + // and then by the existence of a minimum number of peers in the list, matching the minimum number of + // domains in production by High Fidelity. + // + + int MINIMUM_PEERS = 3; + bool IS_HEALTHY = false; + + IS_HEALTHY = _activePeers.size() >= MINIMUM_PEERS ? true : false; + + if (connection->requestOperation() == QNetworkAccessManager::GetOperation) { + if (url.path() == "/status") { + if (IS_HEALTHY) { + connection->respond(HTTPConnection::StatusCode200, QByteArray::number(_activePeers.size())); + } else { + connection->respond(HTTPConnection::StatusCode404, QByteArray::number(_activePeers.size())); + } + } + } + return true; +} + +bool IceServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler) { + return true; +} diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index e15bda1211..effc8b8154 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -12,17 +12,21 @@ #ifndef hifi_IceServer_h #define hifi_IceServer_h -#include -#include -#include +#include +#include +#include #include +#include typedef QHash NetworkPeerHash; -class IceServer : public QCoreApplication { +class IceServer : public QCoreApplication, public HTTPSRequestHandler { + Q_OBJECT public: IceServer(int argc, char* argv[]); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); + bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false); private slots: void processDatagrams(); void clearInactivePeers(); @@ -34,6 +38,8 @@ private: QUdpSocket _serverSocket; NetworkPeerHash _activePeers; QHash > _currentConnections; + HTTPManager _httpManager; + HTTPSManager* _httpsManager; }; #endif // hifi_IceServer_h \ No newline at end of file From 14c7dc8eb9bff4342ebda4b99f27845244aa4ecf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 22 Jan 2015 13:13:18 -0800 Subject: [PATCH 30/59] debugging stutter --- .../entities/src/EntityEditPacketSender.cpp | 7 +++ libraries/entities/src/EntityItem.cpp | 6 +++ libraries/entities/src/EntityTree.cpp | 4 ++ libraries/entities/src/EntityTreeElement.cpp | 2 + libraries/physics/src/EntityMotionState.cpp | 13 +++++ libraries/physics/src/ObjectMotionState.cpp | 49 ++++++++++++++++++- libraries/physics/src/PhysicsEngine.cpp | 3 ++ 7 files changed, 82 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index f2588d0493..9129cd875e 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -27,7 +27,13 @@ void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemID modelID, const EntityItemProperties& properties) { + + qDebug() << "EntityEditPacketSender::queueEditEntityMessage()..."; + qDebug() << " ID:" << modelID; + qDebug() << " properties:" << properties; + if (!_shouldSend) { + qDebug() << " BAIL EARLY! _shouldSend:" << _shouldSend; return; // bail early } @@ -36,6 +42,7 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemI int sizeOut = 0; if (EntityItemProperties::encodeEntityEditPacket(type, modelID, properties, &bufferOut[0], _maxPacketSize, sizeOut)) { + qDebug() << " queueOctreeEditMessage() sizeOut:" << sizeOut; queueOctreeEditMessage(type, bufferOut, sizeOut); } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 7e3e982fb8..b413ea2b23 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1064,6 +1064,9 @@ const float MIN_SPIN_DELTA = 0.0003f; void EntityItem::updatePosition(const glm::vec3& value) { if (glm::distance(_position, value) * (float)TREE_SCALE > MIN_POSITION_DELTA) { + qDebug() << "EntityItem::updatePosition()... "; + qDebug() << " new position:" << value; + qDebug() << " in meters:" << (value * (float) TREE_SCALE); _position = value; recalculateCollisionShape(); _dirtyFlags |= EntityItem::DIRTY_POSITION; @@ -1073,6 +1076,9 @@ void EntityItem::updatePosition(const glm::vec3& value) { void EntityItem::updatePositionInMeters(const glm::vec3& value) { glm::vec3 position = glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f); if (glm::distance(_position, position) * (float)TREE_SCALE > MIN_POSITION_DELTA) { + qDebug() << "EntityItem::updatePositionInMeters()... "; + qDebug() << " new position:" << position; + qDebug() << " in meters:" << value; _position = position; recalculateCollisionShape(); _dirtyFlags |= EntityItem::DIRTY_POSITION; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 580fed8790..1e6fe51916 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -305,6 +305,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) /// we're not changing the content of the tree, we're only changing the internal IDs that map entities from creator /// based to known IDs. This means we don't have to recurse the tree to mark the changed path as dirty. void EntityTree::handleAddEntityResponse(const QByteArray& packet) { + qDebug() << "EntityTree::handleAddEntityResponse()"; if (!getIsClient()) { qDebug() << "UNEXPECTED!!! EntityTree::handleAddEntityResponse() with !getIsClient() ***"; @@ -329,6 +330,9 @@ void EntityTree::handleAddEntityResponse(const QByteArray& packet) { searchEntityID.id = entityID; searchEntityID.creatorTokenID = creatorTokenID; + qDebug() << " creatorTokenID:" << creatorTokenID; + qDebug() << " entityID:" << entityID; + lockForWrite(); // find the creator token version, it's containing element, and the entity itself diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index aff6e64f57..923ab8a9ca 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -795,11 +795,13 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int } } else { + qDebug() << "EntityTreeElement::readElementDataFromBuffer() about to construct entity item"; entityItem = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args); if (entityItem) { bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); addEntityItem(entityItem); // add this new entity to this elements entities entityItemID = entityItem->getEntityItemID(); + qDebug() << " entityItemID:" << entityItemID; _myTree->setContainingElement(entityItemID, this); _myTree->postAddEntity(entityItem); } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8b6fb1ea9f..f08683455c 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -159,15 +159,23 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ return; // never update entities that are unknown } if (_outgoingPacketFlags) { + qDebug() << "EntityMotionState::sendUpdate()..."; + qDebug() << " _outgoingPacketFlags:" << _outgoingPacketFlags; + EntityItemProperties properties = _entity->getProperties(); + qDebug() << " _entity->getProperties():" << properties << "line:" << __LINE__; if (_outgoingPacketFlags & EntityItem::DIRTY_POSITION) { btTransform worldTrans = _body->getWorldTransform(); _sentPosition = bulletToGLM(worldTrans.getOrigin()); properties.setPosition(_sentPosition + ObjectMotionState::getWorldOffset()); + qDebug() << " _sentPosition:" << _sentPosition << "line:" << __LINE__; + qDebug() << " ObjectMotionState::getWorldOffset():" << ObjectMotionState::getWorldOffset() << "line:" << __LINE__; _sentRotation = bulletToGLM(worldTrans.getRotation()); properties.setRotation(_sentRotation); + + qDebug() << " after position properties:" << properties << "line:" << __LINE__; } if (_outgoingPacketFlags & EntityItem::DIRTY_VELOCITY) { @@ -197,6 +205,8 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setGravity(_sentAcceleration); // DANGER! EntityItem stores angularVelocity in degrees/sec!!! properties.setAngularVelocity(glm::degrees(_sentAngularVelocity)); + + qDebug() << " after velocity properties:" << properties << "line:" << __LINE__; } // RELIABLE_SEND_HACK: count number of updates for entities at rest so we can stop sending them after some limit. @@ -217,6 +227,9 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityItemID id(_entity->getID()); EntityEditPacketSender* entityPacketSender = static_cast(packetSender); + qDebug() << "EntityMotionState::sendUpdate()... about to call queueEditEntityMessage()"; + qDebug() << " id:" << id; + qDebug() << " properties:" << properties; entityPacketSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, id, properties); // The outgoing flags only itemized WHAT to send, not WHETHER to send, hence we always set them diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index cab36b8370..cacaab614f 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -108,6 +108,18 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const { bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { assert(_body); + + + // if we've never checked before, our _sentFrame will be 0, and we need to initialize our state + if (_sentFrame == 0) { + _sentPosition = bulletToGLM(_body->getWorldTransform().getOrigin()); + _sentVelocity = bulletToGLM(_body->getLinearVelocity()); + _sentAngularVelocity = bulletToGLM(_body->getAngularVelocity()); + _sentFrame = simulationFrame; + return false; + } + + uint32_t wasSentFrame = _sentFrame; float dt = (float)(simulationFrame - _sentFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP; _sentFrame = simulationFrame; bool isActive = _body->isActive(); @@ -131,6 +143,10 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { // NOTE: math in done the simulation-frame, which is NOT necessarily the same as the world-frame // due to _worldOffset. + + glm::vec3 wasPosition = _sentPosition; + glm::vec3 wasVelocity = _sentVelocity; + glm::vec3 wasAcceleration = _sentAcceleration; // compute position error if (glm::length2(_sentVelocity) > 0.0f) { @@ -141,11 +157,40 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { btTransform worldTrans = _body->getWorldTransform(); glm::vec3 position = bulletToGLM(worldTrans.getOrigin()); - + float dx2 = glm::distance2(position, _sentPosition); const float MAX_POSITION_ERROR_SQUARED = 0.001f; // 0.001 m^2 ~~> 0.03 m if (dx2 > MAX_POSITION_ERROR_SQUARED) { - return true; +qDebug() << "ObjectMotionState::shouldSendUpdate()... computing position error"; + + glm::vec3 bulletVelocity = bulletToGLM(_body->getLinearVelocity()); + +qDebug() << " was _sentFrame:" << wasSentFrame; +qDebug() << " now _sentFrame:" << _sentFrame; +qDebug() << " dt:" << dt; + +qDebug() << " was _sentAcceleration:" << wasAcceleration; +qDebug() << " now _sentAcceleration:" << _sentAcceleration; + +qDebug() << " bulletVelocity:" << bulletVelocity; +qDebug() << " was _sentVelocity:" << wasVelocity; +qDebug() << " now _sentVelocity:" << _sentVelocity; + +qDebug() << " was _sentPosition:" << wasPosition; +qDebug() << " now _sentPosition:" << _sentPosition; +qDebug() << " bullet position:" << position; + +qDebug() << " dx2:" << dx2; +qDebug() << " (dx2 > MAX_POSITION_ERROR_SQUARED)... considering"; + if (wasSentFrame > 0) { +qDebug() << " (wasSentFrame > 0)... return TRUE"; + return true; + } else { +qDebug() << " (wasSentFrame == 0)... ignore use bullet position for next _sentPosition"; + _sentPosition = position; + } + } else { + //qDebug() << " (dx2 <= MAX_POSITION_ERROR_SQUARED)... FALL THROUGH... likely return false"; } if (glm::length2(_sentAngularVelocity) > 0.0f) { diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 7f2b139058..58253d1bd4 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -35,6 +35,9 @@ void PhysicsEngine::updateEntitiesInternal(const quint64& now) { // (3) synchronize outgoing motion states // (4) send outgoing packets + + //qDebug() << "_numSubsteps:" << _numSubsteps; + // this is step (4) QSet::iterator stateItr = _outgoingPackets.begin(); while (stateItr != _outgoingPackets.end()) { From 838a549abefa3549d2f7362686f7c2d2e114276d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 22 Jan 2015 13:31:09 -0800 Subject: [PATCH 31/59] debugging stutter --- .../entities/src/EntityEditPacketSender.cpp | 7 ---- libraries/entities/src/EntityItem.cpp | 6 --- libraries/entities/src/EntityTree.cpp | 4 -- libraries/entities/src/EntityTreeElement.cpp | 2 - libraries/physics/src/EntityMotionState.cpp | 15 -------- libraries/physics/src/ObjectMotionState.cpp | 38 +------------------ libraries/physics/src/PhysicsEngine.cpp | 3 -- 7 files changed, 1 insertion(+), 74 deletions(-) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 9129cd875e..f2588d0493 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -27,13 +27,7 @@ void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemID modelID, const EntityItemProperties& properties) { - - qDebug() << "EntityEditPacketSender::queueEditEntityMessage()..."; - qDebug() << " ID:" << modelID; - qDebug() << " properties:" << properties; - if (!_shouldSend) { - qDebug() << " BAIL EARLY! _shouldSend:" << _shouldSend; return; // bail early } @@ -42,7 +36,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemI int sizeOut = 0; if (EntityItemProperties::encodeEntityEditPacket(type, modelID, properties, &bufferOut[0], _maxPacketSize, sizeOut)) { - qDebug() << " queueOctreeEditMessage() sizeOut:" << sizeOut; queueOctreeEditMessage(type, bufferOut, sizeOut); } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b413ea2b23..7e3e982fb8 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1064,9 +1064,6 @@ const float MIN_SPIN_DELTA = 0.0003f; void EntityItem::updatePosition(const glm::vec3& value) { if (glm::distance(_position, value) * (float)TREE_SCALE > MIN_POSITION_DELTA) { - qDebug() << "EntityItem::updatePosition()... "; - qDebug() << " new position:" << value; - qDebug() << " in meters:" << (value * (float) TREE_SCALE); _position = value; recalculateCollisionShape(); _dirtyFlags |= EntityItem::DIRTY_POSITION; @@ -1076,9 +1073,6 @@ void EntityItem::updatePosition(const glm::vec3& value) { void EntityItem::updatePositionInMeters(const glm::vec3& value) { glm::vec3 position = glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f); if (glm::distance(_position, position) * (float)TREE_SCALE > MIN_POSITION_DELTA) { - qDebug() << "EntityItem::updatePositionInMeters()... "; - qDebug() << " new position:" << position; - qDebug() << " in meters:" << value; _position = position; recalculateCollisionShape(); _dirtyFlags |= EntityItem::DIRTY_POSITION; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 1e6fe51916..580fed8790 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -305,7 +305,6 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) /// we're not changing the content of the tree, we're only changing the internal IDs that map entities from creator /// based to known IDs. This means we don't have to recurse the tree to mark the changed path as dirty. void EntityTree::handleAddEntityResponse(const QByteArray& packet) { - qDebug() << "EntityTree::handleAddEntityResponse()"; if (!getIsClient()) { qDebug() << "UNEXPECTED!!! EntityTree::handleAddEntityResponse() with !getIsClient() ***"; @@ -330,9 +329,6 @@ void EntityTree::handleAddEntityResponse(const QByteArray& packet) { searchEntityID.id = entityID; searchEntityID.creatorTokenID = creatorTokenID; - qDebug() << " creatorTokenID:" << creatorTokenID; - qDebug() << " entityID:" << entityID; - lockForWrite(); // find the creator token version, it's containing element, and the entity itself diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 923ab8a9ca..aff6e64f57 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -795,13 +795,11 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int } } else { - qDebug() << "EntityTreeElement::readElementDataFromBuffer() about to construct entity item"; entityItem = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args); if (entityItem) { bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); addEntityItem(entityItem); // add this new entity to this elements entities entityItemID = entityItem->getEntityItemID(); - qDebug() << " entityItemID:" << entityItemID; _myTree->setContainingElement(entityItemID, this); _myTree->postAddEntity(entityItem); } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index f08683455c..9453501407 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -159,23 +159,13 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ return; // never update entities that are unknown } if (_outgoingPacketFlags) { - qDebug() << "EntityMotionState::sendUpdate()..."; - qDebug() << " _outgoingPacketFlags:" << _outgoingPacketFlags; - EntityItemProperties properties = _entity->getProperties(); - qDebug() << " _entity->getProperties():" << properties << "line:" << __LINE__; - if (_outgoingPacketFlags & EntityItem::DIRTY_POSITION) { btTransform worldTrans = _body->getWorldTransform(); _sentPosition = bulletToGLM(worldTrans.getOrigin()); properties.setPosition(_sentPosition + ObjectMotionState::getWorldOffset()); - qDebug() << " _sentPosition:" << _sentPosition << "line:" << __LINE__; - qDebug() << " ObjectMotionState::getWorldOffset():" << ObjectMotionState::getWorldOffset() << "line:" << __LINE__; - _sentRotation = bulletToGLM(worldTrans.getRotation()); properties.setRotation(_sentRotation); - - qDebug() << " after position properties:" << properties << "line:" << __LINE__; } if (_outgoingPacketFlags & EntityItem::DIRTY_VELOCITY) { @@ -205,8 +195,6 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.setGravity(_sentAcceleration); // DANGER! EntityItem stores angularVelocity in degrees/sec!!! properties.setAngularVelocity(glm::degrees(_sentAngularVelocity)); - - qDebug() << " after velocity properties:" << properties << "line:" << __LINE__; } // RELIABLE_SEND_HACK: count number of updates for entities at rest so we can stop sending them after some limit. @@ -227,9 +215,6 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityItemID id(_entity->getID()); EntityEditPacketSender* entityPacketSender = static_cast(packetSender); - qDebug() << "EntityMotionState::sendUpdate()... about to call queueEditEntityMessage()"; - qDebug() << " id:" << id; - qDebug() << " properties:" << properties; entityPacketSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, id, properties); // The outgoing flags only itemized WHAT to send, not WHETHER to send, hence we always set them diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index cacaab614f..effd7c3e4d 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -109,7 +109,6 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const { bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { assert(_body); - // if we've never checked before, our _sentFrame will be 0, and we need to initialize our state if (_sentFrame == 0) { _sentPosition = bulletToGLM(_body->getWorldTransform().getOrigin()); @@ -119,7 +118,6 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { return false; } - uint32_t wasSentFrame = _sentFrame; float dt = (float)(simulationFrame - _sentFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP; _sentFrame = simulationFrame; bool isActive = _body->isActive(); @@ -143,11 +141,6 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { // NOTE: math in done the simulation-frame, which is NOT necessarily the same as the world-frame // due to _worldOffset. - - glm::vec3 wasPosition = _sentPosition; - glm::vec3 wasVelocity = _sentVelocity; - glm::vec3 wasAcceleration = _sentAcceleration; - // compute position error if (glm::length2(_sentVelocity) > 0.0f) { _sentVelocity += _sentAcceleration * dt; @@ -161,36 +154,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { float dx2 = glm::distance2(position, _sentPosition); const float MAX_POSITION_ERROR_SQUARED = 0.001f; // 0.001 m^2 ~~> 0.03 m if (dx2 > MAX_POSITION_ERROR_SQUARED) { -qDebug() << "ObjectMotionState::shouldSendUpdate()... computing position error"; - - glm::vec3 bulletVelocity = bulletToGLM(_body->getLinearVelocity()); - -qDebug() << " was _sentFrame:" << wasSentFrame; -qDebug() << " now _sentFrame:" << _sentFrame; -qDebug() << " dt:" << dt; - -qDebug() << " was _sentAcceleration:" << wasAcceleration; -qDebug() << " now _sentAcceleration:" << _sentAcceleration; - -qDebug() << " bulletVelocity:" << bulletVelocity; -qDebug() << " was _sentVelocity:" << wasVelocity; -qDebug() << " now _sentVelocity:" << _sentVelocity; - -qDebug() << " was _sentPosition:" << wasPosition; -qDebug() << " now _sentPosition:" << _sentPosition; -qDebug() << " bullet position:" << position; - -qDebug() << " dx2:" << dx2; -qDebug() << " (dx2 > MAX_POSITION_ERROR_SQUARED)... considering"; - if (wasSentFrame > 0) { -qDebug() << " (wasSentFrame > 0)... return TRUE"; - return true; - } else { -qDebug() << " (wasSentFrame == 0)... ignore use bullet position for next _sentPosition"; - _sentPosition = position; - } - } else { - //qDebug() << " (dx2 <= MAX_POSITION_ERROR_SQUARED)... FALL THROUGH... likely return false"; + return true; } if (glm::length2(_sentAngularVelocity) > 0.0f) { diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 58253d1bd4..7f2b139058 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -35,9 +35,6 @@ void PhysicsEngine::updateEntitiesInternal(const quint64& now) { // (3) synchronize outgoing motion states // (4) send outgoing packets - - //qDebug() << "_numSubsteps:" << _numSubsteps; - // this is step (4) QSet::iterator stateItr = _outgoingPackets.begin(); while (stateItr != _outgoingPackets.end()) { From c6ad1462e603a71af72727cdea5c112a80b25784 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 22 Jan 2015 13:31:59 -0800 Subject: [PATCH 32/59] debugging stutter --- libraries/physics/src/EntityMotionState.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9453501407..9be8a8f50e 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -160,10 +160,12 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ } if (_outgoingPacketFlags) { EntityItemProperties properties = _entity->getProperties(); + if (_outgoingPacketFlags & EntityItem::DIRTY_POSITION) { btTransform worldTrans = _body->getWorldTransform(); _sentPosition = bulletToGLM(worldTrans.getOrigin()); properties.setPosition(_sentPosition + ObjectMotionState::getWorldOffset()); + _sentRotation = bulletToGLM(worldTrans.getRotation()); properties.setRotation(_sentRotation); } From 6a9a6968f2ec0e33fbcff24e7f1ddeff5e2db220 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 22 Jan 2015 13:32:57 -0800 Subject: [PATCH 33/59] debugging stutter --- libraries/physics/src/EntityMotionState.cpp | 2 +- libraries/physics/src/ObjectMotionState.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9be8a8f50e..8b6fb1ea9f 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -165,7 +165,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ btTransform worldTrans = _body->getWorldTransform(); _sentPosition = bulletToGLM(worldTrans.getOrigin()); properties.setPosition(_sentPosition + ObjectMotionState::getWorldOffset()); - + _sentRotation = bulletToGLM(worldTrans.getRotation()); properties.setRotation(_sentRotation); } diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index effd7c3e4d..25910556ff 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -141,6 +141,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { // NOTE: math in done the simulation-frame, which is NOT necessarily the same as the world-frame // due to _worldOffset. + // compute position error if (glm::length2(_sentVelocity) > 0.0f) { _sentVelocity += _sentAcceleration * dt; From 162c2f031bd16d5e1a5c33b2397405d6c36463ae Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 22 Jan 2015 15:33:37 -0600 Subject: [PATCH 34/59] Remove https unneeded crust --- ice-server/CMakeLists.txt | 14 -------------- ice-server/src/IceServer.cpp | 7 +------ ice-server/src/IceServer.h | 4 +--- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt index a7b2a206e4..6a8ca5bd9f 100644 --- a/ice-server/CMakeLists.txt +++ b/ice-server/CMakeLists.txt @@ -6,18 +6,4 @@ setup_hifi_project(Network) # link the shared hifi libraries link_hifi_libraries(embedded-webserver networking shared) -# find OpenSSL -find_package(OpenSSL REQUIRED) - -if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") -# this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto -message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." -"\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") -endif () - -include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") - -# append OpenSSL to our list of libraries to link -target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) - include_dependency_includes() \ No newline at end of file diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 531dd4ea22..4648656e87 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -27,8 +27,7 @@ IceServer::IceServer(int argc, char* argv[]) : _id(QUuid::createUuid()), _serverSocket(), _activePeers(), - _httpManager(ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this), - _httpsManager(NULL) + _httpManager(ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this) { // start the ice-server socket qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT; @@ -195,7 +194,3 @@ bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, b } return true; } - -bool IceServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler) { - return true; -} diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index effc8b8154..5367786d01 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -21,12 +21,11 @@ typedef QHash NetworkPeerHash; -class IceServer : public QCoreApplication, public HTTPSRequestHandler { +class IceServer : public QCoreApplication, public HTTPRequestHandler { Q_OBJECT public: IceServer(int argc, char* argv[]); bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); - bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false); private slots: void processDatagrams(); void clearInactivePeers(); @@ -39,7 +38,6 @@ private: NetworkPeerHash _activePeers; QHash > _currentConnections; HTTPManager _httpManager; - HTTPSManager* _httpsManager; }; #endif // hifi_IceServer_h \ No newline at end of file From d34764bd557c811e800b03894b9b9e2319006f5a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 22 Jan 2015 13:33:59 -0800 Subject: [PATCH 35/59] fix whitespace diff --- libraries/physics/src/ObjectMotionState.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 25910556ff..03f4c47bfa 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -141,7 +141,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { // NOTE: math in done the simulation-frame, which is NOT necessarily the same as the world-frame // due to _worldOffset. - + // compute position error if (glm::length2(_sentVelocity) > 0.0f) { _sentVelocity += _sentAcceleration * dt; @@ -151,7 +151,7 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { btTransform worldTrans = _body->getWorldTransform(); glm::vec3 position = bulletToGLM(worldTrans.getOrigin()); - + float dx2 = glm::distance2(position, _sentPosition); const float MAX_POSITION_ERROR_SQUARED = 0.001f; // 0.001 m^2 ~~> 0.03 m if (dx2 > MAX_POSITION_ERROR_SQUARED) { From 001d6896464cf0f6c3123f99c8b6308b816f96eb Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 22 Jan 2015 15:37:58 -0600 Subject: [PATCH 36/59] Fixing includes --- ice-server/src/IceServer.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index 5367786d01..be6d298e3d 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -17,7 +17,8 @@ #include #include -#include +#include +#include typedef QHash NetworkPeerHash; From 59f79fd4368bba983f120d076d63ab4baadede8a Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 22 Jan 2015 13:42:19 -0800 Subject: [PATCH 37/59] Add handedness choice, default to right handed --- examples/controllers/hydra/paddleBall.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index e5d409bd8a..85b025e4cd 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -20,8 +20,16 @@ var PADDLE_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; var GRAVITY = 0.0; var SPRING_FORCE = 15.0; var lastSoundTime = 0; -var controllerID = 1; var gameOn = false; +var leftHanded = false; +var controllerID; + +if (leftHanded) { + controllerID = 1; +} else { + controllerID = 3; +} + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"); From 805d5ace13cf8d5411689776071b734edc8be27a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Jan 2015 14:55:14 -0800 Subject: [PATCH 38/59] Another slight stitching improvement. --- interface/src/MetavoxelSystem.cpp | 87 ++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 2161887b91..e91ff89270 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1540,6 +1540,50 @@ static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1 } } +const int CORNER_COUNT = 4; + +static inline StackArray::Entry getEntry(const StackArray* lineSrc, int stackWidth, int y, float heightfieldHeight, + EdgeCrossing cornerCrossings[CORNER_COUNT], int cornerIndex) { + const EdgeCrossing& cornerCrossing = cornerCrossings[cornerIndex]; + if (cornerCrossing.point.y == 0.0f) { + int offsetX = (cornerIndex & X_MAXIMUM_FLAG) ? 1 : 0; + int offsetZ = (cornerIndex & Y_MAXIMUM_FLAG) ? 1 : 0; + return lineSrc[offsetZ * stackWidth + offsetX].getEntry(y, heightfieldHeight); + } + StackArray::Entry entry; + bool set = false; + if (cornerCrossing.point.y >= y) { + entry.color = cornerCrossing.color; + entry.material = cornerCrossing.material; + set = true; + + if (cornerCrossing.point.y < y + 1) { + entry.setHermiteY(cornerCrossing.normal, cornerCrossing.point.y - y); + } + } else { + entry.material = entry.color = 0; + } + if (!(cornerIndex & X_MAXIMUM_FLAG)) { + const EdgeCrossing& nextCornerCrossingX = cornerCrossings[cornerIndex | X_MAXIMUM_FLAG]; + if (nextCornerCrossingX.point.y != 0.0f && (nextCornerCrossingX.point.y >= y) != set) { + float t = (y - cornerCrossing.point.y) / (nextCornerCrossingX.point.y - cornerCrossing.point.y); + if (t >= 0.0f && t <= 1.0f) { + entry.setHermiteX(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingX.normal, t)), t); + } + } + } + if (!(cornerIndex & Y_MAXIMUM_FLAG)) { + const EdgeCrossing& nextCornerCrossingZ = cornerCrossings[cornerIndex | Y_MAXIMUM_FLAG]; + if (nextCornerCrossingZ.point.y != 0.0f && (nextCornerCrossingZ.point.y >= y) != set) { + float t = (y - cornerCrossing.point.y) / (nextCornerCrossingZ.point.y - cornerCrossing.point.y); + if (t >= 0.0f && t <= 1.0f) { + entry.setHermiteZ(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingZ.normal, t)), t); + } + } + } + return entry; +} + void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { if (!node->getHeight()) { @@ -1748,7 +1792,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g const int LOWER_RIGHT_CORNER = 8; const int NO_CORNERS = 0; const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER; - const int CORNER_COUNT = 4; const int NEXT_CORNERS[] = { 1, 3, 0, 2 }; int corners = NO_CORNERS; if (heightfieldHeight != 0.0f) { @@ -1823,7 +1866,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g indicesZ[x].position = position; indicesZ[x].resize(count); for (int y = position, end = position + count; y < end; y++) { - const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight); + StackArray::Entry entry = getEntry(lineSrc, stackWidth, y, heightfieldHeight, cornerCrossings, 0); if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) { glm::vec3 normal; if (entry.hermiteX != 0) { @@ -1969,10 +2012,13 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // the terrifying conditional code that follows checks each cube edge for a crossing, gathering // its properties (color, material, normal) if one is present; as before, boundary edges are excluded if (crossingCount == 0) { - const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1, heightfieldHeight); + StackArray::Entry nextEntryY = getEntry(lineSrc, stackWidth, y + 1, + heightfieldHeight, cornerCrossings, 0); if (middleX) { - const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y, nextHeightfieldHeightX); - const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, nextHeightfieldHeightX); + StackArray::Entry nextEntryX = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightX, + cornerCrossings, 1); + StackArray::Entry nextEntryXY = getEntry(lineSrc, stackWidth, y + 1, nextHeightfieldHeightX, + cornerCrossings, 1); if (alpha0 != alpha1) { EdgeCrossing& crossing = crossings[crossingCount++]; crossing.point = glm::vec3(entry.getHermiteX(crossing.normal), 0.0f, 0.0f); @@ -1989,12 +2035,12 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY); } if (middleZ) { - const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, - nextHeightfieldHeightZ); - const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry( - y, nextHeightfieldHeightXZ); - const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry( - y + 1, nextHeightfieldHeightXZ); + StackArray::Entry nextEntryZ = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightZ, + cornerCrossings, 2); + StackArray::Entry nextEntryXZ = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightXZ, + cornerCrossings, 3); + StackArray::Entry nextEntryXYZ = getEntry(lineSrc, stackWidth, y + 1, + nextHeightfieldHeightXZ, cornerCrossings, 3); if (alpha1 != alpha5) { EdgeCrossing& crossing = crossings[crossingCount++]; crossing.point = glm::vec3(1.0f, 0.0f, nextEntryX.getHermiteZ(crossing.normal)); @@ -2002,8 +2048,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } if (alpha3 != alpha7) { EdgeCrossing& crossing = crossings[crossingCount++]; - const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, - nextHeightfieldHeightX); + StackArray::Entry nextEntryXY = getEntry(lineSrc, stackWidth, y + 1, + nextHeightfieldHeightX, cornerCrossings, 1); crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal)); crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY); } @@ -2014,15 +2060,15 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } if (alpha5 != alpha7) { EdgeCrossing& crossing = crossings[crossingCount++]; - const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry( - y, nextHeightfieldHeightXZ); + StackArray::Entry nextEntryXZ = getEntry(lineSrc, stackWidth, y, + nextHeightfieldHeightXZ, cornerCrossings, 3); crossing.point = glm::vec3(1.0f, nextEntryXZ.getHermiteY(crossing.normal), 1.0f); crossing.setColorMaterial(alpha5 == 0 ? nextEntryXYZ : nextEntryXZ); } if (alpha6 != alpha7) { EdgeCrossing& crossing = crossings[crossingCount++]; - const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry( - y + 1, nextHeightfieldHeightZ); + StackArray::Entry nextEntryYZ = getEntry(lineSrc, stackWidth, y + 1, + nextHeightfieldHeightZ, cornerCrossings, 2); crossing.point = glm::vec3(nextEntryYZ.getHermiteX(crossing.normal), 1.0f, 1.0f); crossing.setColorMaterial(alpha6 == 0 ? nextEntryXYZ : nextEntryYZ); } @@ -2034,9 +2080,10 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossing.setColorMaterial(alpha0 == 0 ? nextEntryY : entry); } if (middleZ) { - const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ); - const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1, - nextHeightfieldHeightZ); + StackArray::Entry nextEntryZ = getEntry(lineSrc, stackWidth, y, + nextHeightfieldHeightZ, cornerCrossings, 2); + StackArray::Entry nextEntryYZ = getEntry(lineSrc, stackWidth, y + 1, + nextHeightfieldHeightZ, cornerCrossings, 2); if (alpha0 != alpha4) { EdgeCrossing& crossing = crossings[crossingCount++]; crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal)); From 0e6f2d7252f8943897ae1032457a9a45b27494a8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 16:22:36 -0800 Subject: [PATCH 39/59] Update runningScriptsWidgets px -> pt --- interface/ui/runningScriptsWidget.ui | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui index 3d7db5064a..ec078681e3 100644 --- a/interface/ui/runningScriptsWidget.ui +++ b/interface/ui/runningScriptsWidget.ui @@ -70,7 +70,7 @@ Helvetica,Arial,sans-serif - -1 + 16 75 false true @@ -78,7 +78,7 @@ color: #0e7077; -font: bold 16px; +font: bold 16pt; @@ -192,7 +192,7 @@ font: bold 16px; - font: 14px; color: #5f5f5f; margin: 2px; + font: 14pt; color: #5f5f5f; margin: 2px; There are no scripts running. @@ -245,8 +245,8 @@ font: bold 16px; 0 0 - 334 - 20 + 328 + 18 @@ -259,7 +259,7 @@ font: bold 16px; Qt::LeftToRight - font-size: 14px; + font-size: 14pt; @@ -301,7 +301,7 @@ font: bold 16px; - font: 14px; color: #5f5f5f; margin: 2px; + font: 14pt; color: #5f5f5f; margin: 2px; Tip @@ -370,7 +370,7 @@ font: bold 16px; color: #0e7077; -font: bold 16px; +font: bold 16pt; Load Scripts From e65090bab917d781637b2fad748914d759288928 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Jan 2015 21:08:48 -0800 Subject: [PATCH 40/59] Another slight stitching improvement. --- interface/src/MetavoxelSystem.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index e91ff89270..7edabb0beb 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1857,6 +1857,15 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } minimumY = qMin(minimumY, cornerMinimumY); maximumY = qMax(maximumY, cornerMaximumY); + + if (corners == (LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER)) { + appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2], + clampedX, clampedZ, step, vertices, indices, quadIndices); + + } else if (corners == (UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER)) { + appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1], + clampedX, clampedZ, step, vertices, indices, quadIndices); + } } int position = minimumY; int count = maximumY - minimumY + 1; @@ -1948,28 +1957,16 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g case UPPER_RIGHT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: - crossings[crossingCount++] = cornerCrossings[1]; - crossings[crossingCount - 1].point.y -= y; - break; - case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: crossings[crossingCount++] = cornerCrossings[1]; crossings[crossingCount - 1].point.y -= y; - appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2], - clampedX, clampedZ, step, vertices, indices, quadIndices); break; case LOWER_LEFT_CORNER: case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: - crossings[crossingCount++] = cornerCrossings[2]; - crossings[crossingCount - 1].point.y -= y; - break; - case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: crossings[crossingCount++] = cornerCrossings[2]; crossings[crossingCount - 1].point.y -= y; - appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1], - clampedX, clampedZ, step, vertices, indices, quadIndices); break; case LOWER_RIGHT_CORNER: From c124dffd6ab1f3f3988decc684c1457ab3974dbb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Jan 2015 21:26:08 -0800 Subject: [PATCH 41/59] Another slight improvement. --- interface/src/MetavoxelSystem.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 7edabb0beb..1d2caa6c2c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1556,29 +1556,25 @@ static inline StackArray::Entry getEntry(const StackArray* lineSrc, int stackWid entry.color = cornerCrossing.color; entry.material = cornerCrossing.material; set = true; + entry.setHermiteY(cornerCrossing.normal, glm::clamp(cornerCrossing.point.y - y, 0.0f, 1.0f)); - if (cornerCrossing.point.y < y + 1) { - entry.setHermiteY(cornerCrossing.normal, cornerCrossing.point.y - y); - } } else { entry.material = entry.color = 0; } if (!(cornerIndex & X_MAXIMUM_FLAG)) { const EdgeCrossing& nextCornerCrossingX = cornerCrossings[cornerIndex | X_MAXIMUM_FLAG]; if (nextCornerCrossingX.point.y != 0.0f && (nextCornerCrossingX.point.y >= y) != set) { - float t = (y - cornerCrossing.point.y) / (nextCornerCrossingX.point.y - cornerCrossing.point.y); - if (t >= 0.0f && t <= 1.0f) { - entry.setHermiteX(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingX.normal, t)), t); - } + float t = glm::clamp((y - cornerCrossing.point.y) / + (nextCornerCrossingX.point.y - cornerCrossing.point.y), 0.0f, 1.0f); + entry.setHermiteX(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingX.normal, t)), t); } } if (!(cornerIndex & Y_MAXIMUM_FLAG)) { const EdgeCrossing& nextCornerCrossingZ = cornerCrossings[cornerIndex | Y_MAXIMUM_FLAG]; if (nextCornerCrossingZ.point.y != 0.0f && (nextCornerCrossingZ.point.y >= y) != set) { - float t = (y - cornerCrossing.point.y) / (nextCornerCrossingZ.point.y - cornerCrossing.point.y); - if (t >= 0.0f && t <= 1.0f) { - entry.setHermiteZ(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingZ.normal, t)), t); - } + float t = glm::clamp((y - cornerCrossing.point.y) / + (nextCornerCrossingZ.point.y - cornerCrossing.point.y), 0.0f, 1.0f); + entry.setHermiteZ(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingZ.normal, t)), t); } } return entry; From b8a15f0726c90943f07bbc521ab7c37a1fa0bb24 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Jan 2015 22:04:10 -0800 Subject: [PATCH 42/59] Another tweak. --- interface/src/MetavoxelSystem.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 1d2caa6c2c..80c7c3f41f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1542,13 +1542,21 @@ static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1 const int CORNER_COUNT = 4; -static inline StackArray::Entry getEntry(const StackArray* lineSrc, int stackWidth, int y, float heightfieldHeight, +static StackArray::Entry getEntry(const StackArray* lineSrc, int stackWidth, int y, float heightfieldHeight, EdgeCrossing cornerCrossings[CORNER_COUNT], int cornerIndex) { + int offsetX = (cornerIndex & X_MAXIMUM_FLAG) ? 1 : 0; + int offsetZ = (cornerIndex & Y_MAXIMUM_FLAG) ? 1 : 0; + const StackArray& src = lineSrc[offsetZ * stackWidth + offsetX]; + int count = src.getEntryCount(); + if (count > 0) { + int relative = y - src.getPosition(); + if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f)) { + return src.getEntry(y, heightfieldHeight); + } + } const EdgeCrossing& cornerCrossing = cornerCrossings[cornerIndex]; if (cornerCrossing.point.y == 0.0f) { - int offsetX = (cornerIndex & X_MAXIMUM_FLAG) ? 1 : 0; - int offsetZ = (cornerIndex & Y_MAXIMUM_FLAG) ? 1 : 0; - return lineSrc[offsetZ * stackWidth + offsetX].getEntry(y, heightfieldHeight); + return src.getEntry(y, heightfieldHeight); } StackArray::Entry entry; bool set = false; From 31f72107daf7000ab2f54bb214e7e0288bdc7633 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 09:26:48 -0800 Subject: [PATCH 43/59] remove support for old pre-entity svo files --- libraries/entities/src/EntityTree.h | 3 +- libraries/entities/src/ModelEntityItem.cpp | 127 --------------------- libraries/entities/src/ModelEntityItem.h | 3 - 3 files changed, 2 insertions(+), 131 deletions(-) diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index e8c8a93165..405cbecd99 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -60,7 +60,8 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool getWantSVOfileVersions() const { return true; } virtual PacketType expectedDataPacketType() const { return PacketTypeEntityData; } - virtual bool canProcessVersion(PacketVersion thisVersion) const { return true; } // we support all versions + virtual bool canProcessVersion(PacketVersion thisVersion) const + { return thisVersion >= VERSION_ENTITIES_SUPPORT_SPLIT_MTU; } // we support all versions with split mtu virtual bool handlesEditPacketType(PacketType packetType) const; virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 4cdec0f29a..2c3dcd0600 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -81,17 +81,6 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } - - -int ModelEntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { - if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) { - return oldVersionReadEntityDataFromBuffer(data, bytesLeftToRead, args); - } - - // let our base class do most of the work... it will call us back for our porition... - return EntityItem::readEntityDataFromBuffer(data, bytesLeftToRead, args); -} - int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { @@ -131,122 +120,6 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, return bytesRead; } -int ModelEntityItem::oldVersionReadEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { - - int bytesRead = 0; - if (bytesLeftToRead >= expectedBytes()) { - int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; - - const unsigned char* dataAt = data; - - // id - // this old bitstream format had 32bit IDs. They are obsolete and need to be replaced with our new UUID - // format. We can simply read and ignore the old ID since they should not be repeated. This code should only - // run on loading from an old file. - quint32 oldID; - memcpy(&oldID, dataAt, sizeof(oldID)); - dataAt += sizeof(oldID); - bytesRead += sizeof(oldID); - _id = QUuid::createUuid(); - - // _lastUpdated - memcpy(&_lastUpdated, dataAt, sizeof(_lastUpdated)); - dataAt += sizeof(_lastUpdated); - bytesRead += sizeof(_lastUpdated); - _lastUpdated -= clockSkew; - - // _lastEdited - memcpy(&_lastEdited, dataAt, sizeof(_lastEdited)); - dataAt += sizeof(_lastEdited); - bytesRead += sizeof(_lastEdited); - _lastEdited -= clockSkew; - _created = _lastEdited; // NOTE: old models didn't have age or created time, assume their last edit was a create - - QString ageAsString = formatSecondsElapsed(getAge()); - qDebug() << "Loading old model file, _created = _lastEdited =" << _created - << " age=" << getAge() << "seconds - " << ageAsString - << "old ID=" << oldID << "new ID=" << _id; - - // radius - float radius; - memcpy(&radius, dataAt, sizeof(radius)); - dataAt += sizeof(radius); - bytesRead += sizeof(radius); - setRadius(radius); - - // position - memcpy(&_position, dataAt, sizeof(_position)); - dataAt += sizeof(_position); - bytesRead += sizeof(_position); - - // color - memcpy(&_color, dataAt, sizeof(_color)); - dataAt += sizeof(_color); - bytesRead += sizeof(_color); - - // TODO: how to handle this? Presumable, this would only ever be true if the model file was saved with - // a model being in a shouldBeDeleted state. Which seems unlikely. But if it happens, maybe we should delete the entity after loading? - // shouldBeDeleted - bool shouldBeDeleted = false; - memcpy(&shouldBeDeleted, dataAt, sizeof(shouldBeDeleted)); - dataAt += sizeof(shouldBeDeleted); - bytesRead += sizeof(shouldBeDeleted); - if (shouldBeDeleted) { - qDebug() << "UNEXPECTED - read shouldBeDeleted=TRUE from an old format file"; - } - - // modelURL - uint16_t modelURLLength; - memcpy(&modelURLLength, dataAt, sizeof(modelURLLength)); - dataAt += sizeof(modelURLLength); - bytesRead += sizeof(modelURLLength); - QString modelURLString((const char*)dataAt); - setModelURL(modelURLString); - dataAt += modelURLLength; - bytesRead += modelURLLength; - - // rotation - int bytes = unpackOrientationQuatFromBytes(dataAt, _rotation); - dataAt += bytes; - bytesRead += bytes; - - if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ANIMATION) { - // animationURL - uint16_t animationURLLength; - memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); - dataAt += sizeof(animationURLLength); - bytesRead += sizeof(animationURLLength); - QString animationURLString((const char*)dataAt); - setAnimationURL(animationURLString); - dataAt += animationURLLength; - bytesRead += animationURLLength; - - // animationIsPlaying - bool animationIsPlaying; - memcpy(&animationIsPlaying, dataAt, sizeof(animationIsPlaying)); - dataAt += sizeof(animationIsPlaying); - bytesRead += sizeof(animationIsPlaying); - setAnimationIsPlaying(animationIsPlaying); - - // animationFrameIndex - float animationFrameIndex; - memcpy(&animationFrameIndex, dataAt, sizeof(animationFrameIndex)); - dataAt += sizeof(animationFrameIndex); - bytesRead += sizeof(animationFrameIndex); - setAnimationFrameIndex(animationFrameIndex); - - // animationFPS - float animationFPS; - memcpy(&animationFPS, dataAt, sizeof(animationFPS)); - dataAt += sizeof(animationFPS); - bytesRead += sizeof(animationFPS); - setAnimationFPS(animationFPS); - } - } - return bytesRead; -} - - // TODO: eventually only include properties changed since the params.lastViewFrustumSent time EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index dc236644b7..a607745475 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -40,7 +40,6 @@ public: OctreeElement::AppendState& appendState) const; - virtual int readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData); @@ -116,8 +115,6 @@ public: protected: - /// For reading models from pre V3 bitstreams - int oldVersionReadEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); bool isAnimatingSomething() const; rgbColor _color; From d173afaa70dc2bc68cdad9d6b7cfefb4c21afa81 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 11:01:46 -0800 Subject: [PATCH 44/59] add support for non-physical kinematic movement --- libraries/entities/src/EntityItem.cpp | 23 +++- libraries/physics/src/EntityMotionState.cpp | 41 ++++--- libraries/physics/src/EntityMotionState.h | 5 +- libraries/physics/src/ObjectMotionState.cpp | 26 +++-- libraries/physics/src/ObjectMotionState.h | 14 ++- libraries/physics/src/PhysicsEngine.cpp | 119 +++++++++++++++++--- libraries/physics/src/PhysicsEngine.h | 9 +- 7 files changed, 176 insertions(+), 61 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 7e3e982fb8..c830236287 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -520,7 +520,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); recalculateCollisionShape(); - if (overwriteLocalData && (getDirtyFlags() & EntityItem::DIRTY_POSITION)) { + if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) { _lastSimulated = now; } } @@ -770,6 +770,9 @@ void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { float angularSpeed = glm::length(_angularVelocity); const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; // if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { + if (angularSpeed > 0.0f) { + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); } else { // NOTE: angularSpeed is currently in degrees/sec!!! @@ -799,10 +802,18 @@ void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { // "ground" plane of the domain, but for now it's what we've got velocity += getGravity() * timeElapsed; } - - // NOTE: the simulation should NOT set any DirtyFlags on this entity - setPosition(position); // this will automatically recalculate our collision shape - setVelocity(velocity); + + float speed = glm::length(velocity); + const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec + if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { + setVelocity(ENTITY_ITEM_ZERO_VEC3); + if (speed > 0.0f) { + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } + } else { + setPosition(position); // this will automatically recalculate our collision shape + setVelocity(velocity); + } } } @@ -886,7 +897,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { if (_created != UNKNOWN_CREATED_TIME) { setLastEdited(now); } - if (getDirtyFlags() & EntityItem::DIRTY_POSITION) { + if (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) { _lastSimulated = now; } } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8b6fb1ea9f..b4aa9d0e7e 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -14,7 +14,7 @@ #include "BulletUtil.h" #include "EntityMotionState.h" -#include "SimpleEntityKinematicController.h" +#include "PhysicsEngine.h" QSet* _outgoingEntityList; @@ -41,8 +41,6 @@ EntityMotionState::~EntityMotionState() { assert(_entity); _entity->setPhysicsInfo(NULL); _entity = NULL; - delete _kinematicController; - _kinematicController = NULL; } MotionType EntityMotionState::computeMotionType() const { @@ -52,13 +50,15 @@ MotionType EntityMotionState::computeMotionType() const { return _entity->isMoving() ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC; } -void EntityMotionState::addKinematicController() { - if (!_kinematicController) { - _kinematicController = new SimpleEntityKinematicController(_entity); - _kinematicController->start(); - } else { - _kinematicController->start(); - } +void EntityMotionState::updateKinematicState(uint32_t substep) { + setKinematic(_entity->isMoving(), substep); +} + +void EntityMotionState::stepKinematicSimulation(uint32_t substep) { + assert(_isKinematic); + float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; + _entity->simulateSimpleKinematicMotion(dt); + _lastKinematicSubstep = substep; } // This callback is invoked by the physics simulation in two cases: @@ -67,8 +67,11 @@ void EntityMotionState::addKinematicController() { // (2) at the beginning of each simulation frame for KINEMATIC RigidBody's -- // it is an opportunity for outside code to update the object's simulation position void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { - if (_kinematicController && _kinematicController->isRunning()) { - _kinematicController->stepForward(); + if (_isKinematic) { + uint32_t substep = PhysicsEngine::getNumSubsteps(); + // remove const-ness so we can actually update this instance + EntityMotionState* thisMotion = const_cast(this); + thisMotion->stepKinematicSimulation(substep); } worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset())); worldTrans.setRotation(glmToBullet(_entity->getRotation())); @@ -229,12 +232,14 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ uint32_t EntityMotionState::getIncomingDirtyFlags() const { uint32_t dirtyFlags = _entity->getDirtyFlags(); - // we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings - int bodyFlags = _body->getCollisionFlags(); - bool isMoving = _entity->isMoving(); - if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) || - (bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) { - dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + if (_body) { + // we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings + int bodyFlags = _body->getCollisionFlags(); + bool isMoving = _entity->isMoving(); + if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) || + (bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) { + dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } } return dirtyFlags; } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 8eb639688a..192ac166b4 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -14,7 +14,6 @@ #include -#include "KinematicController.h" #include "ObjectMotionState.h" class EntityItem; @@ -39,8 +38,8 @@ public: /// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem MotionType computeMotionType() const; - // virtual override for ObjectMotionState - void addKinematicController(); + void updateKinematicState(uint32_t substep); + void stepKinematicSimulation(uint32_t substep); // this relays incoming position/rotation to the RigidBody void getWorldTransform(btTransform& worldTrans) const; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index cab36b8370..dfa059d47f 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -12,7 +12,6 @@ #include #include "BulletUtil.h" -#include "KinematicController.h" #include "ObjectMotionState.h" #include "PhysicsEngine.h" @@ -56,10 +55,6 @@ ObjectMotionState::ObjectMotionState() : ObjectMotionState::~ObjectMotionState() { // NOTE: you MUST remove this MotionState from the world before you call the dtor. assert(_body == NULL); - if (_kinematicController) { - delete _kinematicController; - _kinematicController = NULL; - } } void ObjectMotionState::setFriction(float friction) { @@ -108,6 +103,15 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const { bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { assert(_body); + // if we've never checked before, our _sentFrame will be 0, and we need to initialize our state + if (_sentFrame == 0) { + _sentPosition = bulletToGLM(_body->getWorldTransform().getOrigin()); + _sentVelocity = bulletToGLM(_body->getLinearVelocity()); + _sentAngularVelocity = bulletToGLM(_body->getAngularVelocity()); + _sentFrame = simulationFrame; + return false; + } + float dt = (float)(simulationFrame - _sentFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP; _sentFrame = simulationFrame; bool isActive = _body->isActive(); @@ -164,13 +168,6 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { return (fabsf(glm::dot(actualRotation, _sentRotation)) < MIN_ROTATION_DOT); } -void ObjectMotionState::removeKinematicController() { - if (_kinematicController) { - delete _kinematicController; - _kinematicController = NULL; - } -} - void ObjectMotionState::setRigidBody(btRigidBody* body) { // give the body a (void*) back-pointer to this ObjectMotionState if (_body != body) { @@ -183,3 +180,8 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { } } } + +void ObjectMotionState::setKinematic(bool kinematic, uint32_t substep) { + _isKinematic = kinematic; + _lastKinematicSubstep = substep; +} diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 223664ceec..424a7fb680 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -46,7 +46,6 @@ const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_POSITION | Entit class OctreeEditPacketSender; -class KinematicController; class ObjectMotionState : public btMotionState { public: @@ -93,11 +92,15 @@ public: virtual MotionType computeMotionType() const = 0; - virtual void addKinematicController() = 0; - virtual void removeKinematicController(); + virtual void updateKinematicState(uint32_t substep) = 0; btRigidBody* getRigidBody() const { return _body; } + bool isKinematic() const { return _isKinematic; } + + void setKinematic(bool kinematic, uint32_t substep); + virtual void stepKinematicSimulation(uint32_t substep) = 0; + friend class PhysicsEngine; protected: void setRigidBody(btRigidBody* body); @@ -114,6 +117,9 @@ protected: btRigidBody* _body; + bool _isKinematic = false; + uint32_t _lastKinematicSubstep = 0; + bool _sentMoving; // true if last update was moving int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects @@ -124,8 +130,6 @@ protected: glm::vec3 _sentVelocity; glm::vec3 _sentAngularVelocity; // radians per second glm::vec3 _sentAcceleration; - - KinematicController* _kinematicController = NULL; }; #endif // hifi_ObjectMotionState_h diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 02304b3b8d..dbeea165c4 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -62,7 +62,13 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) { entity->setPhysicsInfo(static_cast(motionState)); _entityMotionStates.insert(motionState); addObject(shapeInfo, shape, motionState); - } else { + } else if (entity->isMoving()) { + EntityMotionState* motionState = new EntityMotionState(entity); + entity->setPhysicsInfo(static_cast(motionState)); + _entityMotionStates.insert(motionState); + + motionState->setKinematic(true, _numSubsteps); + _nonPhysicalKinematicObjects.insert(motionState); // We failed to add the entity to the simulation. Probably because we couldn't create a shape for it. //qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine"; } @@ -74,10 +80,16 @@ void PhysicsEngine::removeEntityInternal(EntityItem* entity) { void* physicsInfo = entity->getPhysicsInfo(); if (physicsInfo) { EntityMotionState* motionState = static_cast(physicsInfo); - removeObject(motionState); + if (motionState->getRigidBody()) { + removeObject(motionState); + } else { + // only need to hunt in this list when there is no RigidBody + _nonPhysicalKinematicObjects.remove(motionState); + } _entityMotionStates.remove(motionState); _incomingChanges.remove(motionState); _outgoingPackets.remove(motionState); + // NOTE: EntityMotionState dtor will remove its backpointer from EntityItem delete motionState; } } @@ -117,6 +129,7 @@ void PhysicsEngine::clearEntitiesInternal() { delete (*stateItr); } _entityMotionStates.clear(); + _nonPhysicalKinematicObjects.clear(); _incomingChanges.clear(); _outgoingPackets.clear(); } @@ -127,19 +140,75 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { QSet::iterator stateItr = _incomingChanges.begin(); while (stateItr != _incomingChanges.end()) { ObjectMotionState* motionState = *stateItr; + ++stateItr; uint32_t flags = motionState->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; + bool removeMotionState = false; btRigidBody* body = motionState->getRigidBody(); if (body) { if (flags & HARD_DIRTY_PHYSICS_FLAGS) { // a HARD update requires the body be pulled out of physics engine, changed, then reinserted // but it also handles all EASY changes - updateObjectHard(body, motionState, flags); + bool success = updateObjectHard(body, motionState, flags); + if (!success) { + // NOTE: since updateObjectHard() failed we know that motionState has been removed + // from simulation and body has been deleted. Depending on what else has changed + // we might need to remove motionState altogether... + if (flags & EntityItem::DIRTY_VELOCITY) { + motionState->updateKinematicState(_numSubsteps); + if (motionState->isKinematic()) { + // all is NOT lost, we still need to move this object around kinematically + _nonPhysicalKinematicObjects.insert(motionState); + } else { + // no need to keep motionState around + removeMotionState = true; + } + } else { + // no need to keep motionState around + removeMotionState = true; + } + } } else if (flags) { // an EASY update does NOT require that the body be pulled out of physics engine // hence the MotionState has all the knowledge and authority to perform the update. motionState->updateObjectEasy(flags, _numSubsteps); } + } else { + // the only way we should ever get here (motionState exists but no body) is when the object + // is undergoing non-physical kinematic motion. + assert(_nonPhysicalKinematicObjects.contains(motionState)); + + // it is possible that the changes are such that the object can now be added to the physical simulation + if (flags & EntityItem::DIRTY_SHAPE) { + ShapeInfo shapeInfo; + motionState->computeShapeInfo(shapeInfo); + btCollisionShape* shape = _shapeManager.getShape(shapeInfo); + if (shape) { + addObject(shapeInfo, shape, motionState); + _nonPhysicalKinematicObjects.remove(motionState); + } else if (flags & EntityItem::DIRTY_VELOCITY) { + // although we couldn't add the object to the simulation, might need to update kinematic motion... + motionState->updateKinematicState(_numSubsteps); + if (!motionState->isKinematic()) { + _nonPhysicalKinematicObjects.remove(motionState); + removeMotionState = true; + } + } + } else if (flags & EntityItem::DIRTY_VELOCITY) { + // although we still can't add to physics simulation, might need to update kinematic motion... + motionState->updateKinematicState(_numSubsteps); + if (!motionState->isKinematic()) { + _nonPhysicalKinematicObjects.remove(motionState); + removeMotionState = true; + } + } + } + if (removeMotionState) { + // if we get here then there is no need to keep this motionState around (no physics or kinematics) + _outgoingPackets.remove(motionState); + // NOTE: motionState will clean up its own backpointers in the Object + delete motionState; + continue; } // NOTE: the grand order of operations is: @@ -152,7 +221,6 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { // outgoing changes at this point. motionState->clearOutgoingPacketFlags(flags); // clear outgoing flags that were trumped motionState->clearIncomingDirtyFlags(flags); // clear incoming flags that were processed - ++stateItr; } _incomingChanges.clear(); } @@ -229,6 +297,7 @@ void PhysicsEngine::stepSimulation() { // This is step (2). int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); _numSubsteps += (uint32_t)numSubsteps; + stepNonPhysicalKinematics(); unlock(); // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. @@ -248,6 +317,18 @@ void PhysicsEngine::stepSimulation() { computeCollisionEvents(); } +// TODO: need to update non-physical kinematic objects +void PhysicsEngine::stepNonPhysicalKinematics() { + QSet::iterator stateItr = _nonPhysicalKinematicObjects.begin(); + while (stateItr != _nonPhysicalKinematicObjects.end()) { + ObjectMotionState* motionState = *stateItr; + motionState->stepKinematicSimulation(_numSubsteps); + ++stateItr; + } +} + +// TODO?: need to occasionally scan for stopped non-physical kinematics objects + void PhysicsEngine::computeCollisionEvents() { // update all contacts int numManifolds = _collisionDispatcher->getNumManifolds(); @@ -322,7 +403,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->updateInertiaTensor(); motionState->setRigidBody(body); - motionState->addKinematicController(); + motionState->setKinematic(true, _numSubsteps); const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec body->setSleepingThresholds(KINEMATIC_LINEAR_VELOCITY_THRESHOLD, KINEMATIC_ANGULAR_VELOCITY_THRESHOLD); @@ -334,6 +415,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap body = new btRigidBody(mass, motionState, shape, inertia); body->updateInertiaTensor(); motionState->setRigidBody(body); + motionState->setKinematic(false, _numSubsteps); motionState->updateObjectVelocities(); // NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds. // (the 2 seconds is determined by: static btRigidBody::gDeactivationTime @@ -348,6 +430,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); body->updateInertiaTensor(); motionState->setRigidBody(body); + motionState->setKinematic(false, _numSubsteps); break; } } @@ -358,7 +441,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap _dynamicsWorld->addRigidBody(body); } -bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { +void PhysicsEngine::removeObject(ObjectMotionState* motionState) { assert(motionState); btRigidBody* body = motionState->getRigidBody(); if (body) { @@ -369,16 +452,14 @@ bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { _shapeManager.releaseShape(shapeInfo); delete body; motionState->setRigidBody(NULL); - motionState->removeKinematicController(); + motionState->setKinematic(false, _numSubsteps); removeContacts(motionState); - return true; } - return false; } // private -void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) { +bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) { MotionType newType = motionState->computeMotionType(); // pull body out of physics engine @@ -393,7 +474,16 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio ShapeInfo shapeInfo; motionState->computeShapeInfo(shapeInfo); btCollisionShape* newShape = _shapeManager.getShape(shapeInfo); - if (newShape != oldShape) { + if (!newShape) { + // FAIL! we are unable to support these changes! + _shapeManager.releaseShape(oldShape); + + delete body; + motionState->setRigidBody(NULL); + motionState->setKinematic(false, _numSubsteps); + removeContacts(motionState); + return false; + } else if (newShape != oldShape) { // BUG: if shape doesn't change but density does then we won't compute new mass properties // TODO: fix this BUG by replacing DIRTY_MASS with DIRTY_DENSITY and then fix logic accordingly. body->setCollisionShape(newShape); @@ -426,7 +516,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio body->setMassProps(0.0f, btVector3(0.0f, 0.0f, 0.0f)); body->updateInertiaTensor(); - motionState->addKinematicController(); + motionState->setKinematic(true, _numSubsteps); break; } case MOTION_TYPE_DYNAMIC: { @@ -443,7 +533,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio body->updateInertiaTensor(); } body->forceActivationState(ACTIVE_TAG); - motionState->removeKinematicController(); + motionState->setKinematic(false, _numSubsteps); break; } default: { @@ -458,7 +548,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio body->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); body->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f)); - motionState->removeKinematicController(); + motionState->setKinematic(false, _numSubsteps); break; } } @@ -467,4 +557,5 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio _dynamicsWorld->addRigidBody(body); body->activate(); + return true; } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 73a02607e8..9ae9f88e7e 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -67,6 +67,7 @@ public: virtual void init(EntityEditPacketSender* packetSender); void stepSimulation(); + void stepNonPhysicalKinematics(); void computeCollisionEvents(); @@ -81,15 +82,16 @@ public: void addObject(const ShapeInfo& shapeInfo, btCollisionShape* shape, ObjectMotionState* motionState); /// \param motionState pointer to Object's MotionState - /// \return true if Object removed - bool removeObject(ObjectMotionState* motionState); + void removeObject(ObjectMotionState* motionState); /// process queue of changed from external sources void relayIncomingChangesToSimulation(); private: void removeContacts(ObjectMotionState* motionState); - void updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); + + // return 'true' of update was successful + bool updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); btClock _clock; @@ -104,6 +106,7 @@ private: // EntitySimulation stuff QSet _entityMotionStates; // all entities that we track + QSet _nonPhysicalKinematicObjects; // not in physics simulation, but still need kinematic simulation QSet _incomingChanges; // entities with pending physics changes by script or packet QSet _outgoingPackets; // MotionStates with pending changes that need to be sent over wire From eefd32b42b173392150c560b01ece0d57ca6ac6f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 11:04:02 -0800 Subject: [PATCH 45/59] remove KinematicController classes --- libraries/physics/src/KinematicController.cpp | 22 ----------- libraries/physics/src/KinematicController.h | 36 ------------------ .../src/SimpleEntityKinematicController.cpp | 21 ---------- .../src/SimpleEntityKinematicController.h | 38 ------------------- 4 files changed, 117 deletions(-) delete mode 100644 libraries/physics/src/KinematicController.cpp delete mode 100644 libraries/physics/src/KinematicController.h delete mode 100644 libraries/physics/src/SimpleEntityKinematicController.cpp delete mode 100644 libraries/physics/src/SimpleEntityKinematicController.h diff --git a/libraries/physics/src/KinematicController.cpp b/libraries/physics/src/KinematicController.cpp deleted file mode 100644 index 354b285bc1..0000000000 --- a/libraries/physics/src/KinematicController.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// KinematicController.cpp -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// Copyright 2015 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 "KinematicController.h" -#include "PhysicsEngine.h" - -KinematicController::KinematicController() { - _lastSubstep = PhysicsEngine::getNumSubsteps(); -} - -void KinematicController::start() { - _enabled = true; - _lastSubstep = PhysicsEngine::getNumSubsteps(); -} diff --git a/libraries/physics/src/KinematicController.h b/libraries/physics/src/KinematicController.h deleted file mode 100644 index 60b8548607..0000000000 --- a/libraries/physics/src/KinematicController.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// KinematicController.h -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// Copyright 2015 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_KinematicController_h -#define hifi_KinematicController_h - -#include - -/// KinematicController defines an API for derived classes. - -class KinematicController { -public: - KinematicController(); - - virtual ~KinematicController() {} - - virtual void stepForward() = 0; - - void start(); - void stop() { _enabled = false; } - bool isRunning() const { return _enabled; } - -protected: - bool _enabled = false; - uint32_t _lastSubstep; -}; - -#endif // hifi_KinematicController_h diff --git a/libraries/physics/src/SimpleEntityKinematicController.cpp b/libraries/physics/src/SimpleEntityKinematicController.cpp deleted file mode 100644 index e834d4e91b..0000000000 --- a/libraries/physics/src/SimpleEntityKinematicController.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// SimpleEntityKinematicController.cpp -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// Copyright 2015 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 "PhysicsEngine.h" -#include "SimpleEntityKinematicController.h" - -void SimpleEntityKinematicController:: stepForward() { - uint32_t substep = PhysicsEngine::getNumSubsteps(); - float dt = (substep - _lastSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; - _entity->simulateSimpleKinematicMotion(dt); - _lastSubstep = substep; -} - diff --git a/libraries/physics/src/SimpleEntityKinematicController.h b/libraries/physics/src/SimpleEntityKinematicController.h deleted file mode 100644 index 1edfaf8d2c..0000000000 --- a/libraries/physics/src/SimpleEntityKinematicController.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// SimpleEntityKinematicController.h -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// Copyright 2015 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_SimpleEntityKinematicController_h -#define hifi_SimpleEntityKinematicController_h - -/// SimpleKinematicConstroller performs simple exrapolation of velocities. - -#include -#include - -#include - -#include "KinematicController.h" - -class SimpleEntityKinematicController : public KinematicController { -public: - SimpleEntityKinematicController() = delete; // prevent compiler from making a default ctor - - SimpleEntityKinematicController(EntityItem* entity) : KinematicController(), _entity(entity) { assert(entity); } - - ~SimpleEntityKinematicController() { _entity = NULL; } - - void stepForward(); - -private: - EntityItem* _entity; -}; - -#endif // hifi_SimpleEntityKinematicController_h From f2bcdfa2b479cbe917f0afd1d6a127f34694f421 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 13:10:41 -0800 Subject: [PATCH 46/59] update _lastSimulated for kinematic motion --- libraries/entities/src/EntityItem.cpp | 101 ++++---------------- libraries/entities/src/EntityItem.h | 4 +- libraries/physics/src/EntityMotionState.cpp | 20 ++-- libraries/physics/src/EntityMotionState.h | 2 +- libraries/physics/src/ObjectMotionState.h | 2 +- libraries/physics/src/PhysicsEngine.cpp | 7 +- libraries/physics/src/PhysicsEngine.h | 2 +- 7 files changed, 39 insertions(+), 99 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c830236287..c1fc24fdc7 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -607,11 +607,6 @@ bool EntityItem::isRestingOnSurface() const { } void EntityItem::simulate(const quint64& now) { - if (_physicsInfo) { - // we rely on bullet for simulation, so bail - return; - } - bool wantDebug = false; if (_lastSimulated == 0) { @@ -661,9 +656,13 @@ void EntityItem::simulate(const quint64& now) { qDebug() << " ********** EntityItem::simulate() .... SETTING _lastSimulated=" << _lastSimulated; } - if (hasAngularVelocity()) { - glm::quat rotation = getRotation(); + simulateKinematicMotion(timeElapsed); + _lastSimulated = now; +} +void EntityItem::simulateKinematicMotion(float timeElapsed) { + bool wantDebug = false; + if (hasAngularVelocity()) { // angular damping glm::vec3 angularVelocity = getAngularVelocity(); if (_angularDamping > 0.0f) { @@ -679,6 +678,9 @@ void EntityItem::simulate(const quint64& now) { const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; // if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { + if (angularSpeed > 0.0f) { + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); } else { // NOTE: angularSpeed is currently in degrees/sec!!! @@ -686,7 +688,7 @@ void EntityItem::simulate(const quint64& now) { float angle = timeElapsed * glm::radians(angularSpeed); glm::vec3 axis = _angularVelocity / angularSpeed; glm::quat dQ = glm::angleAxis(angle, axis); - rotation = glm::normalize(dQ * rotation); + glm::quat rotation = glm::normalize(dQ * getRotation()); setRotation(rotation); } } @@ -722,80 +724,6 @@ void EntityItem::simulate(const quint64& now) { position = newPosition; - // handle bounces off the ground... We bounce at the distance to the bottom of our entity - if (position.y <= getDistanceToBottomOfEntity()) { - velocity = velocity * glm::vec3(1,-1,1); - position.y = getDistanceToBottomOfEntity(); - } - - // apply gravity - if (hasGravity()) { - // handle resting on surface case, this is definitely a bit of a hack, and it only works on the - // "ground" plane of the domain, but for now it's what we've got - if (isRestingOnSurface()) { - velocity.y = 0.0f; - position.y = getDistanceToBottomOfEntity(); - } else { - velocity += getGravity() * timeElapsed; - } - } - - // NOTE: we don't zero out very small velocities --> we rely on a remote Bullet simulation - // to tell us when the entity has stopped. - - // NOTE: the simulation should NOT set any DirtyFlags on this entity - setPosition(position); // this will automatically recalculate our collision shape - setVelocity(velocity); - - if (wantDebug) { - qDebug() << " new position:" << position; - qDebug() << " new velocity:" << velocity; - qDebug() << " new AACube:" << getMaximumAACube(); - qDebug() << " old getAABox:" << getAABox(); - } - } - - _lastSimulated = now; -} - -void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { - if (hasAngularVelocity()) { - // angular damping - glm::vec3 angularVelocity = getAngularVelocity(); - if (_angularDamping > 0.0f) { - angularVelocity *= powf(1.0f - _angularDamping, timeElapsed); - setAngularVelocity(angularVelocity); - } - - float angularSpeed = glm::length(_angularVelocity); - const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; // - if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { - if (angularSpeed > 0.0f) { - _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; - } - setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); - } else { - // NOTE: angularSpeed is currently in degrees/sec!!! - // TODO: Andrew to convert to radians/sec - float angle = timeElapsed * glm::radians(angularSpeed); - glm::vec3 axis = _angularVelocity / angularSpeed; - glm::quat dQ = glm::angleAxis(angle, axis); - glm::quat rotation = getRotation(); - rotation = glm::normalize(dQ * rotation); - setRotation(rotation); - } - } - - if (hasVelocity()) { - // linear damping - glm::vec3 velocity = getVelocity(); - if (_damping > 0.0f) { - velocity *= powf(1.0f - _damping, timeElapsed); - } - - // integrate position forward - glm::vec3 position = getPosition() + (velocity * timeElapsed); - // apply gravity if (hasGravity()) { // handle resting on surface case, this is definitely a bit of a hack, and it only works on the @@ -811,9 +739,16 @@ void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; } } else { - setPosition(position); // this will automatically recalculate our collision shape + setPosition(position); setVelocity(velocity); } + + if (wantDebug) { + qDebug() << " new position:" << position; + qDebug() << " new velocity:" << velocity; + qDebug() << " new AACube:" << getMaximumAACube(); + qDebug() << " old getAABox:" << getAABox(); + } } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a9a82c5209..d266a30f62 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -82,6 +82,7 @@ public: void recordCreationTime(); // set _created to 'now' quint64 getLastSimulated() const { return _lastSimulated; } /// Last simulated time of this entity universal usecs + void setLastSimulated(quint64 now) { _lastSimulated = now; } /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } @@ -128,9 +129,8 @@ public: // perform linear extrapolation for SimpleEntitySimulation void simulate(const quint64& now); + void simulateKinematicMotion(float timeElapsed); - void simulateSimpleKinematicMotion(float timeElapsed); - virtual bool needsToCallUpdate() const { return false; } virtual void debugDump() const; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index b4aa9d0e7e..f22487b8ea 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -54,11 +54,12 @@ void EntityMotionState::updateKinematicState(uint32_t substep) { setKinematic(_entity->isMoving(), substep); } -void EntityMotionState::stepKinematicSimulation(uint32_t substep) { +void EntityMotionState::stepKinematicSimulation(quint64 now) { assert(_isKinematic); - float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; - _entity->simulateSimpleKinematicMotion(dt); - _lastKinematicSubstep = substep; + // NOTE: this is non-physical kinematic motion which steps to real run-time (now) + // which is different from physical kinematic motion (inside getWorldTransform()) + // which steps in physics simulation time. + _entity->simulate(now); } // This callback is invoked by the physics simulation in two cases: @@ -68,10 +69,15 @@ void EntityMotionState::stepKinematicSimulation(uint32_t substep) { // it is an opportunity for outside code to update the object's simulation position void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { if (_isKinematic) { + // This is physical kinematic motion which steps strictly by the subframe count + // of the physics simulation. uint32_t substep = PhysicsEngine::getNumSubsteps(); - // remove const-ness so we can actually update this instance - EntityMotionState* thisMotion = const_cast(this); - thisMotion->stepKinematicSimulation(substep); + float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; + _entity->simulateKinematicMotion(dt); + _entity->setLastSimulated(usecTimestampNow()); + + // bypass const-ness so we can remember the substep + const_cast(this)->_lastKinematicSubstep = substep; } worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset())); worldTrans.setRotation(glmToBullet(_entity->getRotation())); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 192ac166b4..5d98e545d9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -39,7 +39,7 @@ public: MotionType computeMotionType() const; void updateKinematicState(uint32_t substep); - void stepKinematicSimulation(uint32_t substep); + void stepKinematicSimulation(quint64 now); // this relays incoming position/rotation to the RigidBody void getWorldTransform(btTransform& worldTrans) const; diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 424a7fb680..ceeea219cf 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -99,7 +99,7 @@ public: bool isKinematic() const { return _isKinematic; } void setKinematic(bool kinematic, uint32_t substep); - virtual void stepKinematicSimulation(uint32_t substep) = 0; + virtual void stepKinematicSimulation(quint64 now) = 0; friend class PhysicsEngine; protected: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index dbeea165c4..62693e3c9a 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -297,7 +297,7 @@ void PhysicsEngine::stepSimulation() { // This is step (2). int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); _numSubsteps += (uint32_t)numSubsteps; - stepNonPhysicalKinematics(); + stepNonPhysicalKinematics(usecTimestampNow()); unlock(); // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. @@ -317,12 +317,11 @@ void PhysicsEngine::stepSimulation() { computeCollisionEvents(); } -// TODO: need to update non-physical kinematic objects -void PhysicsEngine::stepNonPhysicalKinematics() { +void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { QSet::iterator stateItr = _nonPhysicalKinematicObjects.begin(); while (stateItr != _nonPhysicalKinematicObjects.end()) { ObjectMotionState* motionState = *stateItr; - motionState->stepKinematicSimulation(_numSubsteps); + motionState->stepKinematicSimulation(now); ++stateItr; } } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 9ae9f88e7e..1dbfe2646e 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -67,7 +67,7 @@ public: virtual void init(EntityEditPacketSender* packetSender); void stepSimulation(); - void stepNonPhysicalKinematics(); + void stepNonPhysicalKinematics(const quint64& now); void computeCollisionEvents(); From 36c20c24fe7f3d90c3bb7d5fd25bfa77edcd6b5d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 13:20:12 -0800 Subject: [PATCH 47/59] fix for velocity in units of domain size --- libraries/entities/src/EntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c1fc24fdc7..1ebde85d65 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -732,7 +732,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed) { } float speed = glm::length(velocity); - const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec + const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f / (float)TREE_SCALE; // 1mm/sec if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { setVelocity(ENTITY_ITEM_ZERO_VEC3); if (speed > 0.0f) { From 3c38a9eb53316e867eadea0ddbec5d766cdbd005 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 13:32:38 -0800 Subject: [PATCH 48/59] add last simulated to the protocal --- libraries/entities/src/EntityItem.cpp | 41 ++++++++++++++++++++-- libraries/entities/src/EntityItem.h | 8 +++-- libraries/networking/src/PacketHeaders.cpp | 2 +- libraries/networking/src/PacketHeaders.h | 1 + 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 7e3e982fb8..cb90875c43 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -140,9 +140,17 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet ByteCountCoded typeCoder = getType(); QByteArray encodedType = typeCoder; - quint64 updateDelta = getLastSimulated() <= getLastEdited() ? 0 : getLastSimulated() - getLastEdited(); + // last updated (animations, non-physics changes) + quint64 updateDelta = getLastUpdated() <= getLastEdited() ? 0 : getLastUpdated() - getLastEdited(); ByteCountCoded updateDeltaCoder = updateDelta; QByteArray encodedUpdateDelta = updateDeltaCoder; + + // last simulated (velocity, angular velocity, physics changes) + quint64 simulatedDelta = getLastSimulated() <= getLastEdited() ? 0 : getLastSimulated() - getLastEdited(); + ByteCountCoded simulatedDeltaCoder = simulatedDelta; + QByteArray encodedSimulatedDelta = simulatedDeltaCoder; + + EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); EntityPropertyFlags propertiesDidntFit = requestedProperties; @@ -170,6 +178,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet bool successCreatedFits = false; bool successLastEditedFits = false; bool successLastUpdatedFits = false; + bool successLastSimulatedFits = false; bool successPropertyFlagsFits = false; int propertyFlagsOffset = 0; int oldPropertyFlagsLength = 0; @@ -189,8 +198,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet if (successLastEditedFits) { successLastUpdatedFits = packetData->appendValue(encodedUpdateDelta); } - if (successLastUpdatedFits) { + successLastSimulatedFits = packetData->appendValue(encodedSimulatedDelta); + } + + if (successLastSimulatedFits) { propertyFlagsOffset = packetData->getUncompressedByteOffset(); encodedPropertyFlags = propertyFlags; oldPropertyFlagsLength = encodedPropertyFlags.length(); @@ -458,6 +470,25 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef encodedUpdateDelta = updateDeltaCoder; // determine true length dataAt += encodedUpdateDelta.size(); bytesRead += encodedUpdateDelta.size(); + + // Newer bitstreams will have a last simulated and a last updated value + if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME) { + // last simulated is stored as ByteCountCoded delta from lastEdited + QByteArray encodedSimulatedDelta = originalDataBuffer.mid(bytesRead); // maximum possible size + ByteCountCoded simulatedDeltaCoder = encodedSimulatedDelta; + quint64 simulatedDelta = simulatedDeltaCoder; + if (overwriteLocalData) { + _lastSimulated = lastEditedFromBufferAdjusted + simulatedDelta; // don't adjust for clock skew since we already did that + if (wantDebug) { + qDebug() << "_lastSimulated =" << _lastSimulated; + qDebug() << "_lastEdited=" << _lastEdited; + qDebug() << "lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted; + } + } + encodedSimulatedDelta = simulatedDeltaCoder; // determine true length + dataAt += encodedSimulatedDelta.size(); + bytesRead += encodedSimulatedDelta.size(); + } // Property Flags QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size @@ -521,6 +552,12 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef recalculateCollisionShape(); if (overwriteLocalData && (getDirtyFlags() & EntityItem::DIRTY_POSITION)) { + // TODO: Andrew & Brad to discuss -- this probably should not be "now" but instead should be the last + // simulated time from server. The logic should maybe be: the position changed from the server, so the + // position we just set can be thought of as the position at the time it was last simulated by the + // server (clock skew adjusted). By setting it to "now" we are saying that the last position is to be + // considered to be the correct position for "now" which is likely in the future from when it actually + // was at that last known positition. _lastSimulated = now; } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a9a82c5209..c68b638553 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -86,7 +86,7 @@ public: /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } void setLastEdited(quint64 lastEdited) - { _lastEdited = _lastUpdated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } + { _lastEdited = _lastUpdated = _lastSimulated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } float getEditedAgo() const /// Elapsed seconds since this entity was last edited { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } @@ -125,6 +125,7 @@ public: // perform update virtual void update(const quint64& now) { _lastUpdated = now; } + quint64 getLastUpdated() const { return _lastUpdated; } // perform linear extrapolation for SimpleEntitySimulation void simulate(const quint64& now); @@ -296,9 +297,10 @@ protected: QUuid _id; uint32_t _creatorTokenID; bool _newlyCreated; - quint64 _lastSimulated; // last time this entity called simulate() - quint64 _lastUpdated; // last time this entity called update() + quint64 _lastSimulated; // last time this entity called simulate(), this includes velocity, angular velocity, and physics changes + quint64 _lastUpdated; // last time this entity called update(), this includes animations and non-physics changes quint64 _lastEdited; // last official local or remote edit time + quint64 _lastEditedFromRemote; // last time we received and edit from the server quint64 _lastEditedFromRemoteInRemoteTime; // last time we received and edit from the server (in server-time-frame) quint64 _created; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 110892a106..07228c8351 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_HAVE_USER_DATA; + return VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 87d93b931f..f0d21ca9f8 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -126,6 +126,7 @@ const PacketVersion VERSION_ENTITIES_HAS_FILE_BREAKS = VERSION_ENTITIES_SUPPORT_ const PacketVersion VERSION_ENTITIES_SUPPORT_DIMENSIONS = 4; const PacketVersion VERSION_ENTITIES_MODELS_HAVE_ANIMATION_SETTINGS = 5; const PacketVersion VERSION_ENTITIES_HAVE_USER_DATA = 6; +const PacketVersion VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME = 7; const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1; #endif // hifi_PacketHeaders_h From fa485d21b7edc7fe08ce4d4e4c0f1fd81d168a4a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 13:52:28 -0800 Subject: [PATCH 49/59] added comment --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cb90875c43..fd6502d045 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -924,6 +924,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { setLastEdited(now); } if (getDirtyFlags() & EntityItem::DIRTY_POSITION) { + // TODO: Andrew & Brad to discuss. Is this correct? Maybe it is. Need to think through all cases. _lastSimulated = now; } } From c01d2d657c242e2e8cc1c7d6c8a493717fbc620f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 13:55:12 -0800 Subject: [PATCH 50/59] added comment --- libraries/physics/src/EntityMotionState.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8b6fb1ea9f..a799a3d7d2 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -208,6 +208,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ if (_numNonMovingUpdates <= 1) { // we only update lastEdited when we're sending new physics data // (i.e. NOT when we just simulate the positions forward, nore when we resend non-moving data) + // NOTE: Andrew & Brad to discuss. Let's make sure we're using lastEdited, lastSimulated, and lastUpdated correctly quint64 lastSimulated = _entity->getLastSimulated(); _entity->setLastEdited(lastSimulated); properties.setLastEdited(lastSimulated); From 9a0576adfb3381e9c7bb5e680018ade4e8e34969 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 23 Jan 2015 15:54:26 -0800 Subject: [PATCH 51/59] remove setting last simulated in setLastEdited() because it may cause problems --- libraries/entities/src/EntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 020f7ee0d4..df619e2f69 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -87,7 +87,7 @@ public: /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } void setLastEdited(quint64 lastEdited) - { _lastEdited = _lastUpdated = _lastSimulated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } + { _lastEdited = _lastUpdated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); } float getEditedAgo() const /// Elapsed seconds since this entity was last edited { return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; } From 7a1300d66cf0ba54e056ba0fa720bb8e1bf61d27 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 23 Jan 2015 16:04:06 -0800 Subject: [PATCH 52/59] Request blocking calls --- examples/controllers/hydra/hydraGrab.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 4d0b873fd2..0c6e135739 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -274,7 +274,7 @@ function controller(wichSide) { this.glowedIntersectingModel.isKnownID = false; } if (!this.grabbing) { - var intersection = Entities.findRayIntersection({ + var intersection = Entities.findRayIntersectionBlocking({ origin: this.palmPosition, direction: this.front }); @@ -304,7 +304,7 @@ function controller(wichSide) { if (this.grabbing) { if (!this.entityID.isKnownID) { print("Unknown grabbed ID " + this.entityID.id + ", isKnown: " + this.entityID.isKnownID); - this.entityID = Entities.findRayIntersection({ + this.entityID = Entities.findRayIntersectionBlocking({ origin: this.palmPosition, direction: this.front }).entityID; @@ -475,7 +475,7 @@ function controller(wichSide) { Vec3.print("Looking at: ", this.palmPosition); var pickRay = { origin: this.palmPosition, direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) }; - var foundIntersection = Entities.findRayIntersection(pickRay); + var foundIntersection = Entities.findRayIntersectionBlocking(pickRay); if(!foundIntersection.accurate) { print("No accurate intersection"); From d26804e68cbe04904b6226499a941fce97a3d5f6 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 26 Jan 2015 13:45:14 -0600 Subject: [PATCH 53/59] Check just for http availability --- ice-server/src/IceServer.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 4648656e87..77deb6125b 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -173,23 +173,12 @@ void IceServer::clearInactivePeers() { bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { // // We need an HTTP handler in order to monitor the health of the ice server - // The correct functioning of the ICE server will first be determined by its HTTP availability, - // and then by the existence of a minimum number of peers in the list, matching the minimum number of - // domains in production by High Fidelity. + // The correct functioning of the ICE server will be determined by its HTTP availability, // - int MINIMUM_PEERS = 3; - bool IS_HEALTHY = false; - - IS_HEALTHY = _activePeers.size() >= MINIMUM_PEERS ? true : false; - if (connection->requestOperation() == QNetworkAccessManager::GetOperation) { if (url.path() == "/status") { - if (IS_HEALTHY) { - connection->respond(HTTPConnection::StatusCode200, QByteArray::number(_activePeers.size())); - } else { - connection->respond(HTTPConnection::StatusCode404, QByteArray::number(_activePeers.size())); - } + connection->respond(HTTPConnection::StatusCode200, QByteArray::number(_activePeers.size())); } } return true; From 9aaa3a6fe46a97b7f9bb9a16310cfaf447521935 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 26 Jan 2015 11:48:44 -0800 Subject: [PATCH 54/59] Temporarily re-enable audio selection on Windows This is to test current behavior on different users' PCs before re-enabling permanently. --- interface/src/Audio.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index ce50e093e4..c7ba02700f 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -118,7 +118,9 @@ void Audio::audioMixerKilled() { QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { QAudioDeviceInfo result; -#ifdef WIN32 +// Temporarily enable audio device selection in Windows again to test how it behaves now +//#ifdef WIN32 +#if FALSE // NOTE // this is a workaround for a windows only QtBug https://bugreports.qt-project.org/browse/QTBUG-16117 // static QAudioDeviceInfo objects get deallocated when QList objects go out of scope From 427840713732101eae349364e031d3bd3fa89ef2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Jan 2015 16:48:33 -0800 Subject: [PATCH 55/59] Account for avatar scale in Hand(Data) get/set When using localToWorld/WorldToLocal type functions --- interface/src/avatar/Hand.cpp | 4 ---- libraries/avatars/src/HandData.cpp | 10 +++++++--- libraries/avatars/src/HandData.h | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 0ea0d1b725..b477f606d7 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -171,10 +171,6 @@ void Hand::renderHandTargets(bool isMine) { glm::vec3 tip = palm.getTipPosition(); glm::vec3 root = palm.getPosition(); - //Scale the positions based on avatar scale - myAvatar->scaleVectorRelativeToPosition(tip); - myAvatar->scaleVectorRelativeToPosition(root); - Avatar::renderJointConnectingCone(root, tip, PALM_FINGER_ROD_RADIUS, PALM_FINGER_ROD_RADIUS); // Render sphere at palm/finger root glm::vec3 offsetFromPalm = root + palm.getNormal() * PALM_DISK_THICKNESS; diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index b691bcc8ef..5d850d06d0 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -27,7 +27,7 @@ HandData::HandData(AvatarData* owningAvatar) : } glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const { - return glm::inverse(getBaseOrientation()) * worldVector; + return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale(); } PalmData& HandData::addNewPalm() { @@ -108,15 +108,19 @@ glm::quat HandData::getBaseOrientation() const { glm::vec3 HandData::getBasePosition() const { return _owningAvatarData->getPosition(); } + +float HandData::getBaseScale() const { + return _owningAvatarData->getTargetScale(); +} glm::vec3 PalmData::getFingerDirection() const { const glm::vec3 LOCAL_FINGER_DIRECTION(0.0f, 0.0f, 1.0f); - return _owningHandData->localToWorldDirection(_rawRotation * LOCAL_FINGER_DIRECTION); + return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_FINGER_DIRECTION)); } glm::vec3 PalmData::getNormal() const { const glm::vec3 LOCAL_PALM_DIRECTION(0.0f, -1.0f, 0.0f); - return _owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_DIRECTION); + return glm::normalize(_owningHandData->localToWorldDirection(_rawRotation * LOCAL_PALM_DIRECTION)); } diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 8e44658faf..534ea67726 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -36,11 +36,11 @@ public: // position conversion glm::vec3 localToWorldPosition(const glm::vec3& localPosition) { - return getBasePosition() + getBaseOrientation() * localPosition; + return getBasePosition() + getBaseOrientation() * localPosition * getBaseScale(); } glm::vec3 localToWorldDirection(const glm::vec3& localVector) { - return getBaseOrientation() * localVector; + return getBaseOrientation() * localVector * getBaseScale(); } glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const; @@ -71,6 +71,7 @@ protected: glm::quat getBaseOrientation() const; glm::vec3 getBasePosition() const; + float getBaseScale() const; private: // privatize copy ctor and assignment operator so copies of this object cannot be made From 4b3291d9468aac4dac6e0885b678fda87f8466fa Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Jan 2015 17:11:49 -0800 Subject: [PATCH 56/59] Some cleanup in hydraGrab.js --- examples/controllers/hydra/hydraGrab.js | 168 +++++++++++++++--------- 1 file changed, 106 insertions(+), 62 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 0c6e135739..5da296f117 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -14,13 +14,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("libraries/entityPropertyDialogBox.js"); +Script.include("../../libraries/entityPropertyDialogBox.js"); var entityPropertyDialogBox = EntityPropertyDialogBox; -var LASER_WIDTH = 4; -var LASER_COLOR = { red: 255, green: 0, blue: 0 }; -var LASER_LENGTH_FACTOR = 500; - var MIN_ANGULAR_SIZE = 2; var MAX_ANGULAR_SIZE = 45; var allowLargeModels = false; @@ -32,7 +28,44 @@ var RIGHT = 1; var jointList = MyAvatar.getJointNames(); -var mode = 0; +var STICKS = 0; +var MAPPED = 1; +var mode = STICKS; + +var LASER_WIDTH = 4; +var LASER_COLOR = [{ red: 200, green: 150, blue: 50 }, // STICKS + { red: 50, green: 150, blue: 200 }]; // MAPPED +var LASER_LENGTH_FACTOR = 500; + +var lastAccurateIntersection = null; +var accurateIntersections = 0; +var totalIntersections = 0; +var inaccurateInARow = 0; +var maxInaccurateInARow = 0; +function getRayIntersection(pickRay) { // pickRay : { origin : {xyz}, direction : {xyz} } + if (lastAccurateIntersection === null) { + lastAccurateIntersection = Entities.findRayIntersectionBlocking(pickRay); + } else { + var intersection = Entities.findRayIntersection(pickRay); + if (intersection.accurate) { + lastAccurateIntersection = intersection; + accurateIntersections++; + maxInaccurateInARow = (maxInaccurateInARow > inaccurateInARow) ? maxInaccurateInARow : inaccurateInARow; + inaccurateInARow = 0; + } else { + inaccurateInARow++; + } + totalIntersections++; + } + return lastAccurateIntersection; +} + +function printIntersectionsStats() { + var ratio = accurateIntersections / totalIntersections; + print("Out of " + totalIntersections + " intersections, " + accurateIntersections + " where accurate. (" + ratio * 100 +"%)"); + print("Worst case was " + maxInaccurateInARow + " inaccurate intersections in a row."); +} + function controller(wichSide) { this.side = wichSide; @@ -42,10 +75,10 @@ function controller(wichSide) { this.bumper = 6 * wichSide + 5; this.oldPalmPosition = Controller.getSpatialControlPosition(this.palm); - this.palmPosition = Controller.getSpatialControlPosition(this.palm); + this.palmPosition = this.oldPalmPosition; this.oldTipPosition = Controller.getSpatialControlPosition(this.tip); - this.tipPosition = Controller.getSpatialControlPosition(this.tip); + this.tipPosition = this.oldTipPosition; this.oldUp = Controller.getSpatialControlNormal(this.palm); this.up = this.oldUp; @@ -81,7 +114,7 @@ function controller(wichSide) { this.laser = Overlays.addOverlay("line3d", { start: { x: 0, y: 0, z: 0 }, end: { x: 0, y: 0, z: 0 }, - color: LASER_COLOR, + color: LASER_COLOR[mode], alpha: 1, visible: false, lineWidth: LASER_WIDTH, @@ -245,9 +278,9 @@ function controller(wichSide) { var inverseRotation = Quat.inverse(MyAvatar.orientation); var startPosition = Vec3.multiplyQbyV(inverseRotation, Vec3.subtract(this.palmPosition, MyAvatar.position)); + startPosition = Vec3.multiply(startPosition, 1 / MyAvatar.scale); var direction = Vec3.multiplyQbyV(inverseRotation, Vec3.subtract(this.tipPosition, this.palmPosition)); - var distance = Vec3.length(direction); - direction = Vec3.multiply(direction, LASER_LENGTH_FACTOR / distance); + direction = Vec3.multiply(direction, LASER_LENGTH_FACTOR / (Vec3.length(direction) * MyAvatar.scale)); var endPosition = Vec3.sum(startPosition, direction); Overlays.editOverlay(this.laser, { @@ -267,17 +300,16 @@ function controller(wichSide) { start: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)), end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale)) }); - this.showLaser(!this.grabbing || mode == 0); + this.showLaser(!this.grabbing || mode == STICKS); if (this.glowedIntersectingModel.isKnownID) { Entities.editEntity(this.glowedIntersectingModel, { glowLevel: 0.0 }); this.glowedIntersectingModel.isKnownID = false; } if (!this.grabbing) { - var intersection = Entities.findRayIntersectionBlocking({ - origin: this.palmPosition, - direction: this.front - }); + var intersection = getRayIntersection({ origin: this.palmPosition, + direction: this.front + }); var halfDiagonal = Vec3.length(intersection.properties.dimensions) / 2.0; @@ -304,17 +336,16 @@ function controller(wichSide) { if (this.grabbing) { if (!this.entityID.isKnownID) { print("Unknown grabbed ID " + this.entityID.id + ", isKnown: " + this.entityID.isKnownID); - this.entityID = Entities.findRayIntersectionBlocking({ - origin: this.palmPosition, - direction: this.front - }).entityID; + this.entityID = getRayIntersection({ origin: this.palmPosition, + direction: this.front + }).entityID; print("Identified ID " + this.entityID.id + ", isKnown: " + this.entityID.isKnownID); } var newPosition; var newRotation; switch (mode) { - case 0: + case STICKS: newPosition = Vec3.sum(this.palmPosition, Vec3.multiply(this.front, this.x)); newPosition = Vec3.sum(newPosition, @@ -328,7 +359,7 @@ function controller(wichSide) { newRotation = Quat.multiply(newRotation, this.oldModelRotation); break; - case 1: + case MAPPED: var forward = Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1 }); var d = Vec3.dot(forward, MyAvatar.position); @@ -397,15 +428,13 @@ function controller(wichSide) { var bumperValue = Controller.isButtonPressed(this.bumper); if (bumperValue && !this.bumperValue) { - if (mode == 0) { - mode = 1; - Overlays.editOverlay(leftController.laser, { color: { red: 0, green: 0, blue: 255 } }); - Overlays.editOverlay(rightController.laser, { color: { red: 0, green: 0, blue: 255 } }); - } else { - mode = 0; - Overlays.editOverlay(leftController.laser, { color: { red: 255, green: 0, blue: 0 } }); - Overlays.editOverlay(rightController.laser, { color: { red: 255, green: 0, blue: 0 } }); + if (mode === STICKS) { + mode = MAPPED; + } else if (mode === MAPPED) { + mode = STICKS; } + Overlays.editOverlay(leftController.laser, { color: LASER_COLOR[mode] }); + Overlays.editOverlay(rightController.laser, { color: LASER_COLOR[mode] }); } this.bumperValue = bumperValue; @@ -475,10 +504,10 @@ function controller(wichSide) { Vec3.print("Looking at: ", this.palmPosition); var pickRay = { origin: this.palmPosition, direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) }; - var foundIntersection = Entities.findRayIntersectionBlocking(pickRay); + var foundIntersection = getRayIntersection(pickRay); - if(!foundIntersection.accurate) { - print("No accurate intersection"); + if(!foundIntersection.intersects) { + print("No intersection"); return; } newModel = foundIntersection.entityID; @@ -526,7 +555,7 @@ function moveEntities() { switch (mode) { - case 0: + case STICKS: var oldLeftPoint = Vec3.sum(leftController.oldPalmPosition, Vec3.multiply(leftController.oldFront, leftController.x)); var oldRightPoint = Vec3.sum(rightController.oldPalmPosition, Vec3.multiply(rightController.oldFront, rightController.x)); @@ -545,7 +574,7 @@ function moveEntities() { newPosition = Vec3.sum(middle, Vec3.multiply(Vec3.subtract(leftController.oldModelPosition, oldMiddle), ratio)); break; - case 1: + case MAPPED: var u = Vec3.normalize(Vec3.subtract(rightController.oldPalmPosition, leftController.oldPalmPosition)); var v = Vec3.normalize(Vec3.subtract(rightController.palmPosition, leftController.palmPosition)); @@ -628,43 +657,56 @@ var glowedEntityID = { id: -1, isKnownID: false }; // In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already // exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that // added it. +var ROOT_MENU = "Edit"; +var ITEM_BEFORE = "Physics"; +var MENU_SEPARATOR = "Models"; +var EDIT_PROPERTIES = "Edit Properties..."; +var INTERSECTION_STATS = "Print Intersection Stats"; +var DELETE = "Delete"; +var LARGE_MODELS = "Allow Selecting of Large Models"; +var SMALL_MODELS = "Allow Selecting of Small Models"; + var LIGHTS = "Allow Selecting of Lights"; + var modelMenuAddedDelete = false; var originalLightsArePickable = Entities.getLightsArePickable(); function setupModelMenus() { print("setupModelMenus()"); // adj our menuitems - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Edit Properties...", - shortcutKeyEvent: { text: "`" }, afterItem: "Models" }); - if (!Menu.menuItemExists("Edit", "Delete")) { + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: MENU_SEPARATOR, isSeparator: true, beforeItem: ITEM_BEFORE }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: EDIT_PROPERTIES, + shortcutKeyEvent: { text: "`" }, afterItem: MENU_SEPARATOR }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: INTERSECTION_STATS, afterItem: MENU_SEPARATOR }); + if (!Menu.menuItemExists(ROOT_MENU, DELETE)) { print("no delete... adding ours"); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", - shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: DELETE, + shortcutKeyEvent: { text: "backspace" }, afterItem: MENU_SEPARATOR }); modelMenuAddedDelete = true; } else { print("delete exists... don't add ours"); } - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L", - afterItem: "Paste Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S", - afterItem: "Allow Selecting of Large Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L", - afterItem: "Allow Selecting of Small Models", isCheckable: true }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: LARGE_MODELS, shortcutKey: "CTRL+META+L", + afterItem: DELETE, isCheckable: true }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: SMALL_MODELS, shortcutKey: "CTRL+META+S", + afterItem: LARGE_MODELS, isCheckable: true }); + Menu.addMenuItem({ menuName: ROOT_MENU, menuItemName: LIGHTS, shortcutKey: "CTRL+SHIFT+META+L", + afterItem: SMALL_MODELS, isCheckable: true }); Entities.setLightsArePickable(false); } function cleanupModelMenus() { - Menu.removeMenuItem("Edit", "Edit Properties..."); + Menu.removeSeparator(ROOT_MENU, MENU_SEPARATOR); + Menu.removeMenuItem(ROOT_MENU, EDIT_PROPERTIES); + Menu.removeMenuItem(ROOT_MENU, INTERSECTION_STATS); if (modelMenuAddedDelete) { // delete our menuitems - Menu.removeMenuItem("Edit", "Delete"); + Menu.removeMenuItem(ROOT_MENU, DELETE); } - Menu.removeMenuItem("Edit", "Allow Selecting of Large Models"); - Menu.removeMenuItem("Edit", "Allow Selecting of Small Models"); - Menu.removeMenuItem("Edit", "Allow Selecting of Lights"); + Menu.removeMenuItem(ROOT_MENU, LARGE_MODELS); + Menu.removeMenuItem(ROOT_MENU, SMALL_MODELS); + Menu.removeMenuItem(ROOT_MENU, LIGHTS); } @@ -688,13 +730,13 @@ function showPropertiesForm(editModelID) { Menu.menuItemEvent.connect(function (menuItem) { print("menuItemEvent() in JS... menuItem=" + menuItem); - if (menuItem == "Allow Selecting of Small Models") { - allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); - } else if (menuItem == "Allow Selecting of Large Models") { - allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models"); - } else if (menuItem == "Allow Selecting of Lights") { - Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights")); - } else if (menuItem == "Delete") { + if (menuItem == SMALL_MODELS) { + allowSmallModels = Menu.isOptionChecked(SMALL_MODELS); + } else if (menuItem == LARGE_MODELS) { + allowLargeModels = Menu.isOptionChecked(LARGE_MODELS); + } else if (menuItem == LIGHTS) { + Entities.setLightsArePickable(Menu.isOptionChecked(LIGHTS)); + } else if (menuItem == DELETE) { if (leftController.grabbing) { print(" Delete Entity.... leftController.entityID="+ leftController.entityID); Entities.deleteEntity(leftController.entityID); @@ -712,7 +754,7 @@ Menu.menuItemEvent.connect(function (menuItem) { } else { print(" Delete Entity.... not holding..."); } - } else if (menuItem == "Edit Properties...") { + } else if (menuItem == EDIT_PROPERTIES) { editModelID = -1; if (leftController.grabbing) { print(" Edit Properties.... leftController.entityID="+ leftController.entityID); @@ -727,16 +769,18 @@ Menu.menuItemEvent.connect(function (menuItem) { print(" Edit Properties.... about to edit properties..."); showPropertiesForm(editModelID); } + } else if (menuItem == INTERSECTION_STATS) { + printIntersectionsStats(); } }); Controller.keyReleaseEvent.connect(function (event) { // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items if (event.text == "`") { - handeMenuEvent("Edit Properties..."); + handeMenuEvent(EDIT_PROPERTIES); } if (event.text == "BACKSPACE") { - handeMenuEvent("Delete"); + handeMenuEvent(DELETE); } }); From 64f849568168faaa6c82b8f2782c8b2465b6f982 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Jan 2015 18:20:08 -0800 Subject: [PATCH 57/59] Fixed mapped rotation + doubled it --- examples/controllers/hydra/hydraGrab.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 5da296f117..202a226f1e 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -108,7 +108,7 @@ function controller(wichSide) { this.positionAtGrab; this.rotationAtGrab; this.modelPositionAtGrab; - this.rotationAtGrab; + this.modelRotationAtGrab; this.jointsIntersectingFromStart = []; this.laser = Overlays.addOverlay("line3d", { @@ -165,7 +165,7 @@ function controller(wichSide) { this.positionAtGrab = this.palmPosition; this.rotationAtGrab = this.rotation; this.modelPositionAtGrab = properties.position; - this.rotationAtGrab = properties.rotation; + this.modelRotationAtGrab = properties.rotation; this.jointsIntersectingFromStart = []; for (var i = 0; i < jointList.length; i++) { var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition); @@ -381,8 +381,9 @@ function controller(wichSide) { newRotation = Quat.multiply(this.rotation, Quat.inverse(this.rotationAtGrab)); + newRotation = Quat.multiply(newRotation, newRotation); newRotation = Quat.multiply(newRotation, - this.rotationAtGrab); + this.modelRotationAtGrab); break; } Entities.editEntity(this.entityID, { @@ -595,11 +596,11 @@ function moveEntities() { leftController.positionAtGrab = leftController.palmPosition; leftController.rotationAtGrab = leftController.rotation; leftController.modelPositionAtGrab = leftController.oldModelPosition; - leftController.rotationAtGrab = rotation; + leftController.modelRotationAtGrab = rotation; rightController.positionAtGrab = rightController.palmPosition; rightController.rotationAtGrab = rightController.rotation; rightController.modelPositionAtGrab = rightController.oldModelPosition; - rightController.rotationAtGrab = rotation; + rightController.modelRotationAtGrab = rotation; break; } Entities.editEntity(leftController.entityID, { From f84f6e9b667a942237fa16f13ea6c1ba4c8ad961 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Jan 2015 19:12:18 -0800 Subject: [PATCH 58/59] Restore joints state on stopAnimation --- libraries/render-utils/src/AnimationHandle.cpp | 11 +++++++++++ libraries/render-utils/src/AnimationHandle.h | 1 + 2 files changed, 12 insertions(+) diff --git a/libraries/render-utils/src/AnimationHandle.cpp b/libraries/render-utils/src/AnimationHandle.cpp index 30edf97a33..64e2bf28b9 100644 --- a/libraries/render-utils/src/AnimationHandle.cpp +++ b/libraries/render-utils/src/AnimationHandle.cpp @@ -74,6 +74,7 @@ void AnimationHandle::setRunning(bool running) { } } else { _model->_runningAnimations.removeOne(_self); + restoreJoints(); replaceMatchingPriorities(0.0f); } emit runningChanged(isRunning()); @@ -173,3 +174,13 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) { } } +void AnimationHandle::restoreJoints() { + for (int i = 0; i < _jointMappings.size(); i++) { + int mapping = _jointMappings.at(i); + if (mapping != -1) { + JointState& state = _model->_jointStates[mapping]; + state.restoreRotation(1.0f, state._animationPriority); + } + } +} + diff --git a/libraries/render-utils/src/AnimationHandle.h b/libraries/render-utils/src/AnimationHandle.h index 3956b01ebf..13a1b97dc1 100644 --- a/libraries/render-utils/src/AnimationHandle.h +++ b/libraries/render-utils/src/AnimationHandle.h @@ -92,6 +92,7 @@ private: void simulate(float deltaTime); void applyFrame(float frameIndex); void replaceMatchingPriorities(float newPriority); + void restoreJoints(); Model* _model; WeakAnimationHandlePointer _self; From cf631bf3fc106b6b330153494cbbb06b7332d1ac Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Jan 2015 19:20:47 -0800 Subject: [PATCH 59/59] Remove unnecessary stopAnimation in script to get rid of sacades --- examples/controllers/hydra/squeezeHands.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/controllers/hydra/squeezeHands.js b/examples/controllers/hydra/squeezeHands.js index 2a4756f017..b1e9274905 100644 --- a/examples/controllers/hydra/squeezeHands.js +++ b/examples/controllers/hydra/squeezeHands.js @@ -60,11 +60,9 @@ Script.update.connect(function(deltaTime) { } if ((leftFrame != lastLeftFrame) && leftHandAnimation.length){ - MyAvatar.stopAnimation(leftHandAnimation); MyAvatar.startAnimation(leftHandAnimation, 30.0, 1.0, false, true, leftFrame, leftFrame); } if ((rightFrame != lastRightFrame) && rightHandAnimation.length) { - MyAvatar.stopAnimation(rightHandAnimation); MyAvatar.startAnimation(rightHandAnimation, 30.0, 1.0, false, true, rightFrame, rightFrame); }