From c036b5fd4ba759a574e24d0463d3f75b16518497 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 3 Jun 2015 10:19:28 -0700 Subject: [PATCH 01/17] Actually commit the changes this time! --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 9 +++++++-- libraries/physics/src/EntityMotionState.cpp | 7 +++++++ libraries/physics/src/EntityMotionState.h | 2 ++ libraries/physics/src/ObjectMotionState.cpp | 3 +++ libraries/physics/src/ObjectMotionState.h | 1 + libraries/physics/src/PhysicsEngine.cpp | 6 ++++-- libraries/shared/src/RegisteredMetaTypes.h | 8 +++++--- 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 23e504bfb8..5e88eae17a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1131,10 +1131,15 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT } const float mass = entity->computeMass(); const float COLLISION_PENTRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity() - const float linearVelocity = glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY; + // The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact, + // but that first contact depends on exactly where we hit in the physics step. + // We can get a more consistent initial-contact energy reading by using the changed velocity. + const float linearVelocity = (collision.type == CONTACT_EVENT_TYPE_START) ? + glm::length(collision.velocityChange) : + glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY; const float energy = mass * linearVelocity * linearVelocity / 2.0f; const glm::vec3 position = collision.contactPoint; - const float COLLISION_ENERGY_AT_FULL_VOLUME = 0.5f; + const float COLLISION_ENERGY_AT_FULL_VOLUME = (collision.type == CONTACT_EVENT_TYPE_START) ? 10.0f : 0.5f; const float COLLISION_MINIMUM_VOLUME = 0.005f; const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9a24aabb34..1f5a1b9f4e 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -494,6 +494,7 @@ void EntityMotionState::measureBodyAcceleration() { float dt = ((float)numSubsteps * PHYSICS_ENGINE_FIXED_SUBSTEP); float invDt = 1.0f / dt; _lastMeasureStep = thisStep; + _measuredDeltaTime = dt; // Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt @@ -502,6 +503,12 @@ void EntityMotionState::measureBodyAcceleration() { _lastVelocity = velocity; } } +glm::vec3 EntityMotionState::getObjectLinearVelocityChange() const { + // This is the dampened change in linear velocity, as calculated in measureBodyAcceleration: dv = a * dt + // It is generally only meaningful during the lifespan of collision. In particular, it is not meaningful + // when the entity first starts moving via direct user action. + return _measuredAcceleration * _measuredDeltaTime; +} // virtual void EntityMotionState::setMotionType(MotionType motionType) { diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 65279dc01a..b38254d42c 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -64,6 +64,7 @@ public: virtual glm::vec3 getObjectLinearVelocity() const { return _entity->getVelocity(); } virtual glm::vec3 getObjectAngularVelocity() const { return _entity->getAngularVelocity(); } virtual glm::vec3 getObjectGravity() const { return _entity->getGravity(); } + virtual glm::vec3 getObjectLinearVelocityChange() const; virtual const QUuid& getObjectID() const { return _entity->getID(); } @@ -101,6 +102,7 @@ protected: uint32_t _lastMeasureStep; glm::vec3 _lastVelocity; glm::vec3 _measuredAcceleration; + float _measuredDeltaTime; quint8 _accelerationNearlyGravityCount; bool _candidateForOwnership; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 57b75f0f3b..67dc8f294c 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -82,6 +82,9 @@ void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const { glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { return bulletToGLM(_body->getLinearVelocity()); } +glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { + return glm::vec3(); // Subclasses override where meaningful. +} glm::vec3 ObjectMotionState::getBodyAngularVelocity() const { return bulletToGLM(_body->getAngularVelocity()); diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 246ed16627..141fbe252b 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -90,6 +90,7 @@ public: glm::vec3 getBodyLinearVelocity() const; glm::vec3 getBodyAngularVelocity() const; + virtual glm::vec3 getObjectLinearVelocityChange() const; virtual uint32_t getAndClearIncomingDirtyFlags() = 0; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index b622a37136..7b9f499925 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -317,6 +317,8 @@ CollisionEvents& PhysicsEngine::getCollisionEvents() { if(type != CONTACT_EVENT_TYPE_CONTINUE || _numSubsteps % CONTINUE_EVENT_FILTER_FREQUENCY == 0) { ObjectMotionState* A = static_cast(contactItr->first._a); ObjectMotionState* B = static_cast(contactItr->first._b); + glm::vec3 velocityChange = (A ? A->getObjectLinearVelocityChange() : glm::vec3(0.0f)) + + (B ? B->getObjectLinearVelocityChange() : glm::vec3(0.0f)); if (A && A->getType() == MOTIONSTATE_TYPE_ENTITY) { QUuid idA = A->getObjectID(); @@ -326,14 +328,14 @@ CollisionEvents& PhysicsEngine::getCollisionEvents() { } glm::vec3 position = bulletToGLM(contact.getPositionWorldOnB()) + _originOffset; glm::vec3 penetration = bulletToGLM(contact.distance * contact.normalWorldOnB); - _collisionEvents.push_back(Collision(type, idA, idB, position, penetration)); + _collisionEvents.push_back(Collision(type, idA, idB, position, penetration, velocityChange)); } else if (B && B->getType() == MOTIONSTATE_TYPE_ENTITY) { QUuid idB = B->getObjectID(); glm::vec3 position = bulletToGLM(contact.getPositionWorldOnA()) + _originOffset; // NOTE: we're flipping the order of A and B (so that the first objectID is never NULL) // hence we must negate the penetration. glm::vec3 penetration = - bulletToGLM(contact.distance * contact.normalWorldOnB); - _collisionEvents.push_back(Collision(type, idB, QUuid(), position, penetration)); + _collisionEvents.push_back(Collision(type, idB, QUuid(), position, penetration, velocityChange)); } } diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 48eecba227..1dcc85107a 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -78,15 +78,17 @@ enum ContactEventType { class Collision { public: - Collision() : type(CONTACT_EVENT_TYPE_START), idA(), idB(), contactPoint(0.0f), penetration(0.0f) { } - Collision(ContactEventType cType, const QUuid& cIdA, const QUuid& cIdB, const glm::vec3& cPoint, const glm::vec3& cPenetration) - : type(cType), idA(cIdA), idB(cIdB), contactPoint(cPoint), penetration(cPenetration) { } + Collision() : type(CONTACT_EVENT_TYPE_START), idA(), idB(), contactPoint(0.0f), penetration(0.0f), velocityChange(0.0f) { } + Collision(ContactEventType cType, const QUuid& cIdA, const QUuid& cIdB, const glm::vec3& cPoint, + const glm::vec3& cPenetration, const glm::vec3& velocityChange) + : type(cType), idA(cIdA), idB(cIdB), contactPoint(cPoint), penetration(cPenetration), velocityChange(velocityChange) { } ContactEventType type; QUuid idA; QUuid idB; glm::vec3 contactPoint; glm::vec3 penetration; + glm::vec3 velocityChange; }; Q_DECLARE_METATYPE(Collision) QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision); From 407746b842b7c3d2aaf01d7cc5ee23962425d58c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 3 Jun 2015 10:38:20 -0700 Subject: [PATCH 02/17] Fix (consistent) typo in non-magic-number constant's name. --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 5e88eae17a..93e302c81b 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1130,13 +1130,13 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT return; } const float mass = entity->computeMass(); - const float COLLISION_PENTRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity() + const float COLLISION_PENETRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity() // The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact, // but that first contact depends on exactly where we hit in the physics step. // We can get a more consistent initial-contact energy reading by using the changed velocity. const float linearVelocity = (collision.type == CONTACT_EVENT_TYPE_START) ? glm::length(collision.velocityChange) : - glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY; + glm::length(collision.penetration) * COLLISION_PENETRATION_TO_VELOCITY; const float energy = mass * linearVelocity * linearVelocity / 2.0f; const glm::vec3 position = collision.contactPoint; const float COLLISION_ENERGY_AT_FULL_VOLUME = (collision.type == CONTACT_EVENT_TYPE_START) ? 10.0f : 0.5f; From 7b6cc59d305379e3918c40de2e4cf003a6fcaf1f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Jun 2015 11:40:48 -0700 Subject: [PATCH 03/17] basic support for collision groups --- interface/src/avatar/AvatarMotionState.cpp | 6 ++ interface/src/avatar/AvatarMotionState.h | 2 + .../src/DynamicCharacterController.cpp | 3 +- libraries/physics/src/EntityMotionState.cpp | 5 ++ libraries/physics/src/EntityMotionState.h | 2 + libraries/physics/src/ObjectMotionState.h | 2 + libraries/physics/src/PhysicsEngine.cpp | 25 +++++++- libraries/physics/src/PhysicsEngine.h | 59 +++++++++++++++++++ 8 files changed, 100 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 530225b319..7a5fb5662c 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -10,6 +10,7 @@ // #include +#include #include "Avatar.h" #include "AvatarMotionState.h" @@ -151,6 +152,11 @@ QUuid AvatarMotionState::getSimulatorID() const { void AvatarMotionState::bump() { } +// virtual +int16_t AvatarMotionState::computeCollisionGroup() { + return COLLISION_GROUP_OTHER_AVATAR; +} + // virtual void AvatarMotionState::clearObjectBackPointer() { ObjectMotionState::clearObjectBackPointer(); diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 68c687d65b..79a4d23179 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -61,6 +61,8 @@ public: void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; } + virtual int16_t computeCollisionGroup(); + friend class AvatarManager; protected: diff --git a/libraries/physics/src/DynamicCharacterController.cpp b/libraries/physics/src/DynamicCharacterController.cpp index 5d2a1798f9..b55975c11a 100644 --- a/libraries/physics/src/DynamicCharacterController.cpp +++ b/libraries/physics/src/DynamicCharacterController.cpp @@ -5,6 +5,7 @@ #include "BulletUtil.h" #include "DynamicCharacterController.h" +#include "PhysicsEngine.h" const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float DEFAULT_GRAVITY = -5.0f; @@ -267,7 +268,7 @@ void DynamicCharacterController::setDynamicsWorld(btDynamicsWorld* world) { if (world && _rigidBody) { _dynamicsWorld = world; _pendingFlags &= ~ PENDING_FLAG_JUMP; - _dynamicsWorld->addRigidBody(_rigidBody); + _dynamicsWorld->addRigidBody(_rigidBody, COLLISION_GROUP_MY_AVATAR, COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addAction(this); //reset(_dynamicsWorld); } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9a24aabb34..c7cbca7ea7 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -517,3 +517,8 @@ QString EntityMotionState::getName() { } return ""; } + +// virtual +int16_t EntityMotionState::computeCollisionGroup() { + return COLLISION_GROUP_DEFAULT; +} diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 65279dc01a..da1a5b5d81 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -77,6 +77,8 @@ public: virtual QString getName(); + virtual int16_t computeCollisionGroup(); + friend class PhysicalEntitySimulation; protected: diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 246ed16627..fc0c58b5da 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -123,6 +123,8 @@ public: virtual QString getName() { return ""; } + virtual int16_t computeCollisionGroup() = 0; + friend class PhysicsEngine; protected: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index b622a37136..5d6823c403 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -23,8 +23,19 @@ uint32_t PhysicsEngine::getNumSubsteps() { } PhysicsEngine::PhysicsEngine(const glm::vec3& offset) : - _originOffset(offset), - _characterController(nullptr) { + _originOffset(offset), + _characterController(nullptr) { + // build table of masks with their group as the key + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_DEFAULT), COLLISION_MASK_DEFAULT); + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_STATIC), COLLISION_MASK_STATIC); + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_KINEMATIC), COLLISION_MASK_KINEMATIC); + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_DEBRIS), COLLISION_MASK_DEBRIS); + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_TRIGGER), COLLISION_MASK_TRIGGER); + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_MY_AVATAR), COLLISION_MASK_MY_AVATAR); + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_MY_ATTACHMENT), COLLISION_MASK_MY_ATTACHMENT); + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_OTHER_AVATAR), COLLISION_MASK_OTHER_AVATAR); + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_OTHER_ATTACHMENT), COLLISION_MASK_OTHER_ATTACHMENT); + _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_COLLISIONLESS), COLLISION_MASK_COLLISIONLESS); } PhysicsEngine::~PhysicsEngine() { @@ -125,7 +136,8 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) { body->setFlags(BT_DISABLE_WORLD_GRAVITY); motionState->updateBodyMaterialProperties(); - _dynamicsWorld->addRigidBody(body); + int16_t group = motionState->computeCollisionGroup(); + _dynamicsWorld->addRigidBody(body, group, getCollisionMask(group)); motionState->getAndClearIncomingDirtyFlags(); } @@ -417,6 +429,7 @@ void PhysicsEngine::setCharacterController(DynamicCharacterController* character } } +// static bool PhysicsEngine::physicsInfoIsActive(void* physicsInfo) { if (!physicsInfo) { return false; @@ -431,6 +444,7 @@ bool PhysicsEngine::physicsInfoIsActive(void* physicsInfo) { return body->isActive(); } +// static bool PhysicsEngine::getBodyLocation(void* physicsInfo, glm::vec3& positionReturn, glm::quat& rotationReturn) { if (!physicsInfo) { return false; @@ -448,3 +462,8 @@ bool PhysicsEngine::getBodyLocation(void* physicsInfo, glm::vec3& positionReturn return true; } + +int16_t PhysicsEngine::getCollisionMask(int16_t group) const { + const int16_t* mask = _collisionMasks.find(btHashInt((int)group)); + return mask ? *mask : COLLISION_MASK_DEFAULT; +} diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 9ff85c9f11..f0bd8ba4e9 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -25,6 +25,61 @@ #include "ObjectMotionState.h" #include "ThreadSafeDynamicsWorld.h" +/* Note: These are the collision groups defined in btBroadphaseProxy. Only + * DefaultFilter and StaticFilter are explicitly used by Bullet (when the + * collision filter of an object is not manually specified), the rest are + * merely suggestions. + * +enum CollisionFilterGroups { + DefaultFilter = 1, + StaticFilter = 2, + KinematicFilter = 4, + DebrisFilter = 8, + SensorTrigger = 16, + CharacterFilter = 32, + AllFilter = -1 +} + * + * When using custom collision filters we pretty much need to do all or nothing. + * We'll be doing it all which means we define our own groups and build custom masks + * for everything. + * +*/ + +const int16_t COLLISION_GROUP_DEFAULT = 1 << 0; +const int16_t COLLISION_GROUP_STATIC = 1 << 1; +const int16_t COLLISION_GROUP_KINEMATIC = 1 << 2; +const int16_t COLLISION_GROUP_DEBRIS = 1 << 3; +const int16_t COLLISION_GROUP_TRIGGER = 1 << 4; +const int16_t COLLISION_GROUP_MY_AVATAR = 1 << 5; +const int16_t COLLISION_GROUP_OTHER_AVATAR = 1 << 6; +const int16_t COLLISION_GROUP_MY_ATTACHMENT = 1 << 7; +const int16_t COLLISION_GROUP_OTHER_ATTACHMENT = 1 << 8; +// ... +const int16_t COLLISION_GROUP_COLLISIONLESS = 1 << 15; + + +/* Note: In order for objectA to collide with objectB at the filter stage + * both (groupA & maskB) and (groupB & maskA) must be non-zero. + */ +// DEFAULT collides with everything except COLLISIONLESS +const int16_t COLLISION_MASK_DEFAULT = ~ COLLISION_GROUP_COLLISIONLESS; +const int16_t COLLISION_MASK_STATIC = COLLISION_MASK_DEFAULT; +const int16_t COLLISION_MASK_KINEMATIC = COLLISION_MASK_DEFAULT; +// DEBRIS also doesn't collide with: other DEBRIS, and TRIGGER +const int16_t COLLISION_MASK_DEBRIS = ~ (COLLISION_GROUP_COLLISIONLESS + | COLLISION_GROUP_DEBRIS + | COLLISION_GROUP_TRIGGER); +// TRIGGER also doesn't collide with: DEBRIS, TRIGGER, and STATIC (TRIGGER only detects moveable things that matter) +const int16_t COLLISION_MASK_TRIGGER = COLLISION_MASK_DEBRIS & ~(COLLISION_GROUP_STATIC); +// AVATAR also doesn't collide with: corresponding ATTACHMENT +const int16_t COLLISION_MASK_MY_AVATAR = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_MY_ATTACHMENT); +const int16_t COLLISION_MASK_MY_ATTACHMENT = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_MY_AVATAR); +const int16_t COLLISION_MASK_OTHER_AVATAR = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_OTHER_ATTACHMENT); +const int16_t COLLISION_MASK_OTHER_ATTACHMENT = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_OTHER_AVATAR); +// ... +const int16_t COLLISION_MASK_COLLISIONLESS = 0; + const float HALF_SIMULATION_EXTENT = 512.0f; // meters // simple class for keeping track of contacts @@ -91,9 +146,12 @@ public: void dumpNextStats() { _dumpNextStats = true; } + // TODO: Andrew to move these to ObjectMotionState static bool physicsInfoIsActive(void* physicsInfo); static bool getBodyLocation(void* physicsInfo, glm::vec3& positionReturn, glm::quat& rotationReturn); + int16_t getCollisionMask(int16_t group) const; + private: void removeContacts(ObjectMotionState* motionState); @@ -121,6 +179,7 @@ private: QUuid _sessionID; CollisionEvents _collisionEvents; + btHashMap _collisionMasks; }; #endif // hifi_PhysicsEngine_h From a989998816b9b62f1d9933f17f86943b4d20f675 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Jun 2015 14:13:30 -0700 Subject: [PATCH 04/17] fix typo in comment --- libraries/shared/src/PhysicsHelpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/PhysicsHelpers.cpp b/libraries/shared/src/PhysicsHelpers.cpp index be06920325..578c85683b 100644 --- a/libraries/shared/src/PhysicsHelpers.cpp +++ b/libraries/shared/src/PhysicsHelpers.cpp @@ -5,7 +5,7 @@ // Created by Andrew Meadows 2015.01.27 // Unless otherwise copyrighted: Copyright 2015 High Fidelity, Inc. // -// Unless otherwise licensced: Distributed under the Apache License, Version 2.0. +// Unless otherwise licensed: Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // From ec0b4a956aad62f12f0c4051ebffe6c6a640ca6f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Jun 2015 14:13:51 -0700 Subject: [PATCH 05/17] move collision group consts to shared lib --- interface/src/avatar/AvatarMotionState.cpp | 2 +- .../src/DynamicCharacterController.cpp | 3 +- libraries/physics/src/EntityMotionState.cpp | 1 + libraries/physics/src/PhysicsEngine.cpp | 2 + libraries/physics/src/PhysicsEngine.h | 55 ------------- libraries/shared/src/PhysicsCollisionGroups.h | 79 +++++++++++++++++++ 6 files changed, 85 insertions(+), 57 deletions(-) create mode 100644 libraries/shared/src/PhysicsCollisionGroups.h diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 7a5fb5662c..06da384a94 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -10,7 +10,7 @@ // #include -#include +#include #include "Avatar.h" #include "AvatarMotionState.h" diff --git a/libraries/physics/src/DynamicCharacterController.cpp b/libraries/physics/src/DynamicCharacterController.cpp index b55975c11a..1fca236f63 100644 --- a/libraries/physics/src/DynamicCharacterController.cpp +++ b/libraries/physics/src/DynamicCharacterController.cpp @@ -3,9 +3,10 @@ #include #include +#include + #include "BulletUtil.h" #include "DynamicCharacterController.h" -#include "PhysicsEngine.h" const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float DEFAULT_GRAVITY = -5.0f; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c7cbca7ea7..9bac73c945 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "BulletUtil.h" #include "EntityMotionState.h" diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 5d6823c403..c27eaafdfd 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "ObjectMotionState.h" #include "PhysicsEngine.h" #include "PhysicsHelpers.h" diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index f0bd8ba4e9..edc6615a26 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -25,61 +25,6 @@ #include "ObjectMotionState.h" #include "ThreadSafeDynamicsWorld.h" -/* Note: These are the collision groups defined in btBroadphaseProxy. Only - * DefaultFilter and StaticFilter are explicitly used by Bullet (when the - * collision filter of an object is not manually specified), the rest are - * merely suggestions. - * -enum CollisionFilterGroups { - DefaultFilter = 1, - StaticFilter = 2, - KinematicFilter = 4, - DebrisFilter = 8, - SensorTrigger = 16, - CharacterFilter = 32, - AllFilter = -1 -} - * - * When using custom collision filters we pretty much need to do all or nothing. - * We'll be doing it all which means we define our own groups and build custom masks - * for everything. - * -*/ - -const int16_t COLLISION_GROUP_DEFAULT = 1 << 0; -const int16_t COLLISION_GROUP_STATIC = 1 << 1; -const int16_t COLLISION_GROUP_KINEMATIC = 1 << 2; -const int16_t COLLISION_GROUP_DEBRIS = 1 << 3; -const int16_t COLLISION_GROUP_TRIGGER = 1 << 4; -const int16_t COLLISION_GROUP_MY_AVATAR = 1 << 5; -const int16_t COLLISION_GROUP_OTHER_AVATAR = 1 << 6; -const int16_t COLLISION_GROUP_MY_ATTACHMENT = 1 << 7; -const int16_t COLLISION_GROUP_OTHER_ATTACHMENT = 1 << 8; -// ... -const int16_t COLLISION_GROUP_COLLISIONLESS = 1 << 15; - - -/* Note: In order for objectA to collide with objectB at the filter stage - * both (groupA & maskB) and (groupB & maskA) must be non-zero. - */ -// DEFAULT collides with everything except COLLISIONLESS -const int16_t COLLISION_MASK_DEFAULT = ~ COLLISION_GROUP_COLLISIONLESS; -const int16_t COLLISION_MASK_STATIC = COLLISION_MASK_DEFAULT; -const int16_t COLLISION_MASK_KINEMATIC = COLLISION_MASK_DEFAULT; -// DEBRIS also doesn't collide with: other DEBRIS, and TRIGGER -const int16_t COLLISION_MASK_DEBRIS = ~ (COLLISION_GROUP_COLLISIONLESS - | COLLISION_GROUP_DEBRIS - | COLLISION_GROUP_TRIGGER); -// TRIGGER also doesn't collide with: DEBRIS, TRIGGER, and STATIC (TRIGGER only detects moveable things that matter) -const int16_t COLLISION_MASK_TRIGGER = COLLISION_MASK_DEBRIS & ~(COLLISION_GROUP_STATIC); -// AVATAR also doesn't collide with: corresponding ATTACHMENT -const int16_t COLLISION_MASK_MY_AVATAR = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_MY_ATTACHMENT); -const int16_t COLLISION_MASK_MY_ATTACHMENT = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_MY_AVATAR); -const int16_t COLLISION_MASK_OTHER_AVATAR = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_OTHER_ATTACHMENT); -const int16_t COLLISION_MASK_OTHER_ATTACHMENT = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_OTHER_AVATAR); -// ... -const int16_t COLLISION_MASK_COLLISIONLESS = 0; - const float HALF_SIMULATION_EXTENT = 512.0f; // meters // simple class for keeping track of contacts diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h new file mode 100644 index 0000000000..cce9637cd4 --- /dev/null +++ b/libraries/shared/src/PhysicsCollisionGroups.h @@ -0,0 +1,79 @@ +// +// PhysicsCollisionGroups.h +// libraries/shared/src +// +// Created by Andrew Meadows 2015.06.03 +// 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_PhysicsCollisionGroups_h +#define hifi_PhysicsCollisionGroups_h + +#include + +/* Note: These are the Bullet collision groups defined in btBroadphaseProxy. Only + * DefaultFilter and StaticFilter are explicitly used by Bullet (when the collision + * filter of an object is not manually specified), the rest are merely suggestions. + * +enum CollisionFilterGroups { + DefaultFilter = 1, + StaticFilter = 2, + KinematicFilter = 4, + DebrisFilter = 8, + SensorTrigger = 16, + CharacterFilter = 32, + AllFilter = -1 +} + * + * When using custom collision filters we pretty much need to do all or nothing. + * We'll be doing it all which means we define our own groups and build custom masks + * for everything. + * +*/ + +const int16_t COLLISION_GROUP_DEFAULT = 1 << 0; +const int16_t COLLISION_GROUP_STATIC = 1 << 1; +const int16_t COLLISION_GROUP_KINEMATIC = 1 << 2; +const int16_t COLLISION_GROUP_DEBRIS = 1 << 3; +const int16_t COLLISION_GROUP_TRIGGER = 1 << 4; +const int16_t COLLISION_GROUP_MY_AVATAR = 1 << 5; +const int16_t COLLISION_GROUP_OTHER_AVATAR = 1 << 6; +const int16_t COLLISION_GROUP_MY_ATTACHMENT = 1 << 7; +const int16_t COLLISION_GROUP_OTHER_ATTACHMENT = 1 << 8; +// ... +const int16_t COLLISION_GROUP_COLLISIONLESS = 1 << 15; + + +/* Note: In order for objectA to collide with objectB at the filter stage + * both (groupA & maskB) and (groupB & maskA) must be non-zero. + */ + +// DEFAULT collides with everything except COLLISIONLESS +const int16_t COLLISION_MASK_DEFAULT = ~ COLLISION_GROUP_COLLISIONLESS; + +// STATIC also doesn't collide with other STATIC +const int16_t COLLISION_MASK_STATIC = ~ (COLLISION_GROUP_COLLISIONLESS | COLLISION_MASK_STATIC); + +const int16_t COLLISION_MASK_KINEMATIC = COLLISION_MASK_DEFAULT; + +// DEBRIS also doesn't collide with other DEBRIS, or TRIGGER +const int16_t COLLISION_MASK_DEBRIS = ~ (COLLISION_GROUP_COLLISIONLESS + | COLLISION_GROUP_DEBRIS + | COLLISION_GROUP_TRIGGER); + +// TRIGGER also doesn't collide with DEBRIS, TRIGGER, or STATIC (TRIGGER only detects moveable things that matter) +const int16_t COLLISION_MASK_TRIGGER = COLLISION_MASK_DEBRIS & ~(COLLISION_GROUP_STATIC); + +// AVATAR also doesn't collide with corresponding ATTACHMENTs +const int16_t COLLISION_MASK_MY_AVATAR = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_MY_ATTACHMENT); +const int16_t COLLISION_MASK_MY_ATTACHMENT = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_MY_AVATAR); +const int16_t COLLISION_MASK_OTHER_AVATAR = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_OTHER_ATTACHMENT); +const int16_t COLLISION_MASK_OTHER_ATTACHMENT = ~(COLLISION_GROUP_COLLISIONLESS | COLLISION_GROUP_OTHER_AVATAR); + +// COLLISIONLESS gets an empty mask. +const int16_t COLLISION_MASK_COLLISIONLESS = 0; + +#endif // hifi_PhysicsCollisionGroups_h From 8e61a10bcf365c24924a635c8ad2c48d8f4ca30f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Jun 2015 14:22:48 -0700 Subject: [PATCH 06/17] compute collision group for entity --- libraries/physics/src/EntityMotionState.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9bac73c945..f2c86edd1f 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -521,5 +521,13 @@ QString EntityMotionState::getName() { // virtual int16_t EntityMotionState::computeCollisionGroup() { + switch (computeObjectMotionType()){ + case MOTION_TYPE_STATIC: + return COLLISION_GROUP_STATIC; + case MOTION_TYPE_KINEMATIC: + return COLLISION_GROUP_KINEMATIC; + default: + break; + } return COLLISION_GROUP_DEFAULT; } From b0ca2df3aa52e4b73242bd8ba173a13c49f910d8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 3 Jun 2015 14:56:04 -0700 Subject: [PATCH 07/17] move API from PhysicsEngine to ObjectMotionState --- .../src/RenderableDebugableEntityItem.cpp | 3 +- libraries/physics/src/ObjectMotionState.cpp | 1 + libraries/physics/src/ObjectMotionState.h | 2 ++ libraries/physics/src/PhysicsEngine.cpp | 34 ------------------- libraries/physics/src/PhysicsEngine.h | 4 --- 5 files changed, 5 insertions(+), 39 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp index 017cb289f0..7854aee82f 100644 --- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp @@ -80,7 +80,8 @@ void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) renderBoundingBox(entity, args, 0.3f, yellowColor); } - if (PhysicsEngine::physicsInfoIsActive(entity->getPhysicsInfo())) { + ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState && motionState->isActive()) { renderHoverDot(entity, args); } } diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 57b75f0f3b..e2f248ead7 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -200,3 +200,4 @@ void ObjectMotionState::updateBodyMassProperties() { _body->setMassProps(mass, inertia); _body->updateInertiaTensor(); } + diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index fc0c58b5da..4a9fd769c8 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -125,6 +125,8 @@ public: virtual int16_t computeCollisionGroup() = 0; + bool isActive() const { return _body ? _body->isActive() : false; } + friend class PhysicsEngine; protected: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index c27eaafdfd..b6a509a874 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -431,40 +431,6 @@ void PhysicsEngine::setCharacterController(DynamicCharacterController* character } } -// static -bool PhysicsEngine::physicsInfoIsActive(void* physicsInfo) { - if (!physicsInfo) { - return false; - } - - ObjectMotionState* motionState = static_cast(physicsInfo); - btRigidBody* body = motionState->getRigidBody(); - if (!body) { - return false; - } - - return body->isActive(); -} - -// static -bool PhysicsEngine::getBodyLocation(void* physicsInfo, glm::vec3& positionReturn, glm::quat& rotationReturn) { - if (!physicsInfo) { - return false; - } - - ObjectMotionState* motionState = static_cast(physicsInfo); - btRigidBody* body = motionState->getRigidBody(); - if (!body) { - return false; - } - - const btTransform& worldTrans = body->getCenterOfMassTransform(); - positionReturn = bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(); - rotationReturn = bulletToGLM(worldTrans.getRotation()); - - return true; -} - int16_t PhysicsEngine::getCollisionMask(int16_t group) const { const int16_t* mask = _collisionMasks.find(btHashInt((int)group)); return mask ? *mask : COLLISION_MASK_DEFAULT; diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index edc6615a26..db00a2ba01 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -91,10 +91,6 @@ public: void dumpNextStats() { _dumpNextStats = true; } - // TODO: Andrew to move these to ObjectMotionState - static bool physicsInfoIsActive(void* physicsInfo); - static bool getBodyLocation(void* physicsInfo, glm::vec3& positionReturn, glm::quat& rotationReturn); - int16_t getCollisionMask(int16_t group) const; private: From 5727400b9af023e8fbcea3a19e0740205c991de2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 4 Jun 2015 14:20:19 -0700 Subject: [PATCH 08/17] Sound toys script (in examples/soundToys.js) --- examples/example/soundToys.js | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 examples/example/soundToys.js diff --git a/examples/example/soundToys.js b/examples/example/soundToys.js new file mode 100644 index 0000000000..9d8c824b90 --- /dev/null +++ b/examples/example/soundToys.js @@ -0,0 +1,66 @@ +"use strict"; +// Creates some objects that each play a sound when they are hit (or when they hit something else). +// +// Created by Howard Stearns on June 3, 2015 +// 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 + +var Camera, Vec3, Quat, Entities, Script; // Globals defined by HiFi, var'ed here to keep jslint happy. +var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +var SOUND_BUCKET = "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/"; +var MAX_ANGULAR_SPEED = Math.PI; +var N_EACH_OBJECTS = 3; + +var ourToys = []; +function deleteAll() { + ourToys.forEach(Entities.deleteEntity); +} +function makeAll() { + var currentPosition = Vec3.sum(Camera.getPosition(), Vec3.multiply(4, Quat.getFront(Camera.getOrientation()))), + right = Vec3.multiply(0.6, Quat.getRight(Camera.getOrientation())), + currentDimensions, + data = [ + ["models/props/Dice/goldDie.fbx", HIFI_PUBLIC_BUCKET + "sounds/dice/diceCollide.wav"], + ["models/props/Pool/ball_8.fbx", HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"], + ["eric/models/woodFloor.fbx", SOUND_BUCKET + "67LCollision05.wav"] + ]; + currentPosition = Vec3.sum(currentPosition, Vec3.multiply(-1 * data.length * N_EACH_OBJECTS / 2, right)); + function makeOne(model, sound) { + var thisEntity; + function dropOnce() { // Once gravity is added, it will work if picked up and again dropped. + Entities.editEntity(thisEntity, {gravity: {x: 0, y: -9.8, z: 0}}); + Script.removeEventHandler(thisEntity, 'clickDownOnEntity', dropOnce); + } + thisEntity = Entities.addEntity({ + type: "Model", + modelURL: HIFI_PUBLIC_BUCKET + model, + collisionSoundURL: sound, + collisionsWillMove: true, + shapeType: "box", + restitution: 0.8, + dimensions: currentDimensions, + position: currentPosition, + angularVelocity: { + x: Math.random() * MAX_ANGULAR_SPEED, + y: Math.random() * MAX_ANGULAR_SPEED, + z: Math.random() * MAX_ANGULAR_SPEED + } + }); + ourToys.push(thisEntity); + Script.addEventHandler(thisEntity, 'clickDownOnEntity', dropOnce); + currentDimensions = Vec3.multiply(currentDimensions, 2); + currentPosition = Vec3.sum(currentPosition, right); + } + function makeThree(modelSound) { + var i, model = modelSound[0], sound = modelSound[1]; + currentDimensions = {x: 0.1, y: 0.1, z: 0.1}; + for (i = 0; i < N_EACH_OBJECTS; i++) { + makeOne(model, sound); + } + } + data.forEach(makeThree); +} +makeAll(); +Script.scriptEnding.connect(deleteAll); From 5cbb5dadca469653cf3e7b33ecb80fa779ff678f Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 4 Jun 2015 14:21:12 -0700 Subject: [PATCH 09/17] More perspicacious zero. --- libraries/physics/src/ObjectMotionState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 67dc8f294c..f7fc044c92 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -83,7 +83,7 @@ glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { return bulletToGLM(_body->getLinearVelocity()); } glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { - return glm::vec3(); // Subclasses override where meaningful. + return glm::vec3(0.0f); // Subclasses override where meaningful. } glm::vec3 ObjectMotionState::getBodyAngularVelocity() const { From 407d3b6a6eff1461d54dd6ff499b3d5e40bd94a5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 4 Jun 2015 14:23:51 -0700 Subject: [PATCH 10/17] Pitch is dependent on (largest dimension of) minimum bounding, not max. Adjust sound range of full volume. --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 93e302c81b..7fda8214bb 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1134,12 +1134,14 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT // The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact, // but that first contact depends on exactly where we hit in the physics step. // We can get a more consistent initial-contact energy reading by using the changed velocity. + // Note that velocityChange is not a good indicator for continuing collisions, because it does not distinguish + // between bounce and sliding along a surface. const float linearVelocity = (collision.type == CONTACT_EVENT_TYPE_START) ? glm::length(collision.velocityChange) : glm::length(collision.penetration) * COLLISION_PENETRATION_TO_VELOCITY; const float energy = mass * linearVelocity * linearVelocity / 2.0f; const glm::vec3 position = collision.contactPoint; - const float COLLISION_ENERGY_AT_FULL_VOLUME = (collision.type == CONTACT_EVENT_TYPE_START) ? 10.0f : 0.5f; + const float COLLISION_ENERGY_AT_FULL_VOLUME = (collision.type == CONTACT_EVENT_TYPE_START) ? 150.0f : 5.0f; const float COLLISION_MINIMUM_VOLUME = 0.005f; const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) { @@ -1172,7 +1174,7 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT soxr_io_spec_t spec = soxr_io_spec(SOXR_INT16_I, SOXR_INT16_I); soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0); const int channelCount = sound->isStereo() ? 2 : 1; - const float factor = log(1.0f + (entity->getMaximumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2); + const float factor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2); const int standardRate = AudioConstants::SAMPLE_RATE; const int resampledRate = standardRate * factor; const int nInputSamples = samples.size() / sizeof(int16_t); From a2ae51371be4e39cae6620b03d39069989bc642b Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 4 Jun 2015 15:44:39 -0700 Subject: [PATCH 11/17] updated pointer.js script to work with new line entity --- examples/pointer.js | 159 +++++++++----------------------------------- 1 file changed, 30 insertions(+), 129 deletions(-) diff --git a/examples/pointer.js b/examples/pointer.js index 32698209a4..5b293c8478 100644 --- a/examples/pointer.js +++ b/examples/pointer.js @@ -1,30 +1,17 @@ // pointer.js // examples // -// Created by Eric Levin on May 26, 2015 +// Created by Seth Alves on May 15th +// Modified by Eric Levin on June 4 // Copyright 2015 High Fidelity, Inc. // // Provides a pointer with option to draw on surfaces // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// var lineEntityID = null; var lineIsRezzed = false; -var altHeld = false; -var lineCreated = false; -var position, positionOffset, prevPosition; -var nearLinePosition; -var strokes = []; -var STROKE_ADJUST = 0.005; -var DISTANCE_DRAW_THRESHOLD = .02; -var drawDistance = 0; - -var LINE_WIDTH = 20; - -var userCanPoint = false; -var userCanDraw = false; var BUTTON_SIZE = 32; var PADDING = 3; @@ -43,16 +30,7 @@ var buttonOnColor = { HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; var screenSize = Controller.getViewportDimensions(); -var drawButton = Overlays.addOverlay("image", { - x: screenSize.x / 2 - BUTTON_SIZE + PADDING * 2, - y: screenSize.y - (BUTTON_SIZE + PADDING), - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: HIFI_PUBLIC_BUCKET + "images/pencil.png?v2", - color: buttonOffColor, - alpha: 1 -}); - +var userCanPoint = false; var pointerButton = Overlays.addOverlay("image", { x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, y: screenSize.y - (BUTTON_SIZE + PADDING), @@ -61,14 +39,12 @@ var pointerButton = Overlays.addOverlay("image", { imageURL: HIFI_PUBLIC_BUCKET + "images/laser.png", color: buttonOffColor, alpha: 1 -}) +}); -var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2.0, Quat.getFront(Camera.getOrientation()))); -center.y += 0.5; -function calculateNearLinePosition(targetPosition) { +function nearLinePoint(targetPosition) { var handPosition = MyAvatar.getRightPalmPosition(); var along = Vec3.subtract(targetPosition, handPosition); along = Vec3.normalize(along); @@ -87,39 +63,40 @@ function removeLine() { function createOrUpdateLine(event) { - if (!userCanPoint) { - return; - } var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking var props = Entities.getEntityProperties(intersection.entityID); - if (intersection.intersects) { - startPosition = intersection.intersection; - var subtractVec = Vec3.multiply(Vec3.normalize(pickRay.direction), STROKE_ADJUST); - startPosition = Vec3.subtract(startPosition, subtractVec); - nearLinePosition = calculateNearLinePosition(intersection.intersection); - positionOffset = Vec3.subtract(startPosition, nearLinePosition); + if (intersection.intersects && userCanPoint) { + var points = [nearLinePoint(intersection.intersection), intersection.intersection] if (lineIsRezzed) { Entities.editEntity(lineEntityID, { - position: nearLinePosition, - dimensions: positionOffset, + position: nearLinePoint(intersection.intersection), + linePoints: points, + dimensions: { + x: 1, + y: 1, + z: 1 + }, + lifetime: 15 + props.lifespan // renew lifetime }); - if (userCanDraw) { - draw(); - } } else { lineIsRezzed = true; - prevPosition = startPosition; lineEntityID = Entities.addEntity({ type: "Line", - position: nearLinePosition, - dimensions: positionOffset, + position: nearLinePoint(intersection.intersection), + linePoints: points, + dimensions: { + x: 1, + y: 1, + z: 1 + }, color: { red: 255, green: 255, blue: 255 }, + lifetime: 15 // if someone crashes while pointing, don't leave the line there forever. }); } } else { @@ -127,120 +104,44 @@ function createOrUpdateLine(event) { } } -function draw() { - //We only want to draw line if distance between starting and previous point is large enough - drawDistance = Vec3.distance(startPosition, prevPosition); - if (drawDistance < DISTANCE_DRAW_THRESHOLD) { +function mousePressEvent(event) { + if (!event.isLeftButton) { return; } - var offset = Vec3.subtract(startPosition, prevPosition); - strokes.push(Entities.addEntity({ - type: "Line", - position: prevPosition, - dimensions: offset, - color: { - red: 200, - green: 40, - blue: 200 - }, - lineWidth: LINE_WIDTH - })); - prevPosition = startPosition; -} - -function mousePressEvent(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({ + createOrUpdateLine(event); + var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - if (clickedOverlay == drawButton) { - userCanDraw = !userCanDraw; - if (userCanDraw === true) { - Overlays.editOverlay(drawButton, { - color: buttonOnColor - }); - } else { - Overlays.editOverlay(drawButton, { - color: buttonOffColor - }); - } - } - if (clickedOverlay == pointerButton) { userCanPoint = !userCanPoint; if (userCanPoint === true) { Overlays.editOverlay(pointerButton, { color: buttonOnColor }); - if (userCanDraw === true) { - - Overlays.editOverlay(drawButton, { - color: buttonOnColor - }); - } } else { Overlays.editOverlay(pointerButton, { color: buttonOffColor }); - Overlays.editOverlay(drawButton, { - color: buttonOffColor - }); } } - - if (!event.isLeftButton || altHeld) { - return; - } - Controller.mouseMoveEvent.connect(mouseMoveEvent); - createOrUpdateLine(event); - lineCreated = true; } - function mouseMoveEvent(event) { createOrUpdateLine(event); } - function mouseReleaseEvent(event) { - if (!lineCreated) { + if (!event.isLeftButton) { return; } - Controller.mouseMoveEvent.disconnect(mouseMoveEvent); removeLine(); - lineCreated = false; } -function keyPressEvent(event) { - if (event.text == "ALT") { - altHeld = true; - } -} - -function keyReleaseEvent(event) { - if (event.text == "ALT") { - altHeld = false; - } - -} - -function cleanup() { - for (var i = 0; i < strokes.length; i++) { - Entities.deleteEntity(strokes[i]); - } - - Overlays.deleteOverlay(drawButton); - Overlays.deleteOverlay(pointerButton); -} - - -Script.scriptEnding.connect(cleanup); +Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); - -Controller.keyPressEvent.connect(keyPressEvent); -Controller.keyReleaseEvent.connect(keyReleaseEvent); \ No newline at end of file +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); \ No newline at end of file From 1fd0650955295101e5cb78775f43e187ecc1bbfa Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 4 Jun 2015 16:54:52 -0700 Subject: [PATCH 12/17] removing overlay on script ending --- examples/pointer.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/pointer.js b/examples/pointer.js index 5b293c8478..cca46709ee 100644 --- a/examples/pointer.js +++ b/examples/pointer.js @@ -142,6 +142,12 @@ function mouseReleaseEvent(event) { removeLine(); } +function cleanup() { + Overlays.deleteOverlay(pointerButton); +} + +Script.scriptEnding.connect(cleanup); + Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); \ No newline at end of file From 241330442c1575bc69dcceab4df590cf6f66d746 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 4 Jun 2015 17:35:26 -0700 Subject: [PATCH 13/17] added paint.js and line rider script. Users can paint lines with hydra or mouse, and then go on a ride over the line they just created. --- examples/lineRider.js | 117 +++++++++++ examples/paint.js | 478 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 595 insertions(+) create mode 100644 examples/lineRider.js create mode 100644 examples/paint.js diff --git a/examples/lineRider.js b/examples/lineRider.js new file mode 100644 index 0000000000..21a690fc63 --- /dev/null +++ b/examples/lineRider.js @@ -0,0 +1,117 @@ +// +// lineRider.js +// examples +// +// Created by Eric Levin on 6/4/15. +// Copyright 2014 High Fidelity, Inc. +// +// Takes the avatar on a line ride. Meant to be used in conjunction with paint.js +// Paint a line and then click on roller coaster icon to start! +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +LineRider = function() { + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + var screenSize = Controller.getViewportDimensions(); + + var BUTTON_SIZE = 32; + var PADDING = 3; + + this.buttonOffColor = { + red: 250, + green: 10, + blue: 10 + }; + this.buttonOnColor = { + red: 10, + green: 200, + blue: 100 + }; + this.riding = false; + + this.startButton = Overlays.addOverlay("image", { + x: screenSize.x / 2 - BUTTON_SIZE + PADDING * 2, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/coaster.png?v2", + color: this.buttonOffColor, + alpha: 1 + }); + + this.currentPoint = 0; + this.shouldUpdate = false; + this.moveIntervalTime = 50; + +} + + +LineRider.prototype.move = function() { + if (!this.shouldMove) { + return; + } + MyAvatar.position = this.points[this.currentPoint++]; + + if (this.currentPoint === this.points.length) { + this.currentPoint = 0; + } + var self = this; + Script.setTimeout(function() { + self.move(); + }, this.moveIntervalTime); +} + +LineRider.prototype.setPath = function(points) { + this.points = points; +} + +LineRider.prototype.addStartHandler = function(callback) { + this.onStart = callback; +} + + +LineRider.prototype.mousePressEvent = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + if (clickedOverlay == this.startButton) { + this.toggleRide(); + + } + +} + +LineRider.prototype.toggleRide = function() { + this.riding = !this.riding; + if (this.riding === true) { + Overlays.editOverlay(this.startButton, { + color: this.buttonOnColor + }); + if (this.onStart) { + this.onStart(); + //make sure we actually have a path + if (this.points.length > 2) { + this.shouldMove = true; + } + var self = this; + Script.setTimeout(function() { + self.move(); + }, this.moveIntervalTime); + } + } else { + Overlays.editOverlay(this.startButton, { + color: this.buttonOffColor + }) + this.shouldMove = false; + } + +} +LineRider.prototype.startRide = function() { + this.shouldUpdate = true; + +} + +LineRider.prototype.cleanup = function() { + Overlays.deleteOverlay(this.startButton); +} \ No newline at end of file diff --git a/examples/paint.js b/examples/paint.js new file mode 100644 index 0000000000..9a774a1442 --- /dev/null +++ b/examples/paint.js @@ -0,0 +1,478 @@ +// +// paint.js +// examples +// +// Created by Eric Levin on 6/4/15. +// Copyright 2014 High Fidelity, Inc. +// +// This script allows you to paint with the hydra or mouse! +// +// +// 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('lineRider.js') +var MAX_POINTS_PER_LINE = 30; +var DRAWING_DISTANCE = 5; + +var colorPalette = [{ + red: 236, + green: 208, + blue: 120 +}, { + red: 217, + green: 91, + blue: 67 +}, { + red: 192, + green: 41, + blue: 66 +}, { + red: 84, + green: 36, + blue: 55 +}, { + red: 83, + green: 119, + blue: 122 +}]; + +var currentColorIndex = 0; +var currentColor = colorPalette[currentColorIndex]; + + + +if (hydraCheck() === true) { + HydraPaint(); +} else { + MousePaint(); +} + + +function cycleColor() { + currentColor = colorPalette[++currentColorIndex]; + if (currentColorIndex === colorPalette.length - 1) { + currentColorIndex = -1; + } + +} + +function hydraCheck() { + var numberOfButtons = Controller.getNumberOfButtons(); + var numberOfTriggers = Controller.getNumberOfTriggers(); + var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); + var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; + hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2); + return hydrasConnected; //hydrasConnected; +} + + +function MousePaint() { + var lines = []; + var isDrawing = false; + var path = []; + + var lineRider = new LineRider(); + lineRider.addStartHandler(function() { + var points = []; + //create points array from list of all points in path + path.forEach(function(point) { + points.push(point); + }); + lineRider.setPath(points); + }); + + + + var LINE_WIDTH = 7; + var line; + var points = []; + + + var BRUSH_SIZE = 0.08; + + var brush = Entities.addEntity({ + type: 'Sphere', + position: { + x: 0, + y: 0, + z: 0 + }, + color: currentColor, + dimensions: { + x: BRUSH_SIZE, + y: BRUSH_SIZE, + z: BRUSH_SIZE + } + }); + + + function newLine(point) { + line = Entities.addEntity({ + position: MyAvatar.position, + type: "Line", + color: currentColor, + dimensions: { + x: 10, + y: 10, + z: 10 + }, + lineWidth: LINE_WIDTH + }); + points = []; + if (point) { + points.push(point); + path.push(point); + } + lines.push(line); + } + + + function mouseMoveEvent(event) { + if (!isDrawing) { + return; + } + + + var pickRay = Camera.computePickRay(event.x, event.y); + var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE); + var point = Vec3.sum(Camera.getPosition(), addVector); + points.push(point); + Entities.editEntity(line, { + linePoints: points + }); + Entities.editEntity(brush, { + position: point + }); + path.push(point); + + if (points.length === MAX_POINTS_PER_LINE) { + //We need to start a new line! + newLine(point); + } + } + + function mousePressEvent(event) { + lineRider.mousePressEvent(event); + path = []; + newLine(); + isDrawing = true; + + + } + + function mouseReleaseEvent() { + isDrawing = false; + } + + function keyPressEvent(event) { + if (event.text === "SPACE") { + cycleColor(); + Entities.editEntity(brush, { + color: currentColor + }); + } + } + + function cleanup() { + lines.forEach(function(line) { + Entities.deleteEntity(line); + }); + Entities.deleteEntity(brush); + + } + + + Controller.mousePressEvent.connect(mousePressEvent); + Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + Controller.mouseMoveEvent.connect(mouseMoveEvent); + Script.scriptEnding.connect(cleanup); + + function randFloat(low, high) { + return Math.floor(low + Math.random() * (high - low)); + } + + + function randInt(low, high) { + return Math.floor(randFloat(low, high)); + } + + Controller.keyPressEvent.connect(keyPressEvent); +} + + + +//*****************HYDRA PAINT ******************************************* + + + + +function HydraPaint() { + + + + var lineRider = new LineRider(); + lineRider.addStartHandler(function() { + var points = []; + //create points array from list of all points in path + rightController.path.forEach(function(point) { + points.push(point); + }); + lineRider.setPath(points); + }); + + var LEFT = 0; + var RIGHT = 1; + + var currentTime = 0; + + + var DISTANCE_FROM_HAND = 2; + var minBrushSize = .02; + var maxBrushSize = .04 + + + var minLineWidth = 5; + var maxLineWidth = 10; + var currentLineWidth = minLineWidth; + var MIN_PAINT_TRIGGER_THRESHOLD = .01; + var LINE_LIFETIME = 20; + var COLOR_CHANGE_TIME_FACTOR = 0.1; + + var RIGHT_BUTTON_1 = 7 + var RIGHT_BUTTON_2 = 8 + var RIGHT_BUTTON_3 = 9; + var RIGHT_BUTTON_4 = 10 + + var LEFT_BUTTON_1 = 1; + var LEFT_BUTTON_2 = 2; + var LEFT_BUTTON_3 = 3; + var LEFT_BUTTON_4 = 4; + + var STROKE_SMOOTH_FACTOR = 1; + + var MIN_DRAW_DISTANCE = 1; + var MAX_DRAW_DISTANCE = 2; + + function controller(side, undoButton, redoButton, cycleColorButton, startRideButton) { + this.triggerHeld = false; + this.triggerThreshold = 0.9; + this.side = side; + this.palm = 2 * side; + this.tip = 2 * side + 1; + this.trigger = side; + this.lines = []; + this.deletedLines = [] //just an array of properties objects + this.isPainting = false; + + this.undoButton = undoButton; + this.undoButtonPressed = false; + this.prevUndoButtonPressed = false; + + this.redoButton = redoButton; + this.redoButtonPressed = false; + this.prevRedoButtonPressed = false; + + this.cycleColorButton = cycleColorButton; + this.cycleColorButtonPressed = false; + this.prevColorCycleButtonPressed = false; + + this.startRideButton = startRideButton; + this.startRideButtonPressed = false; + this.prevStartRideButtonPressed = false; + + this.strokeCount = 0; + this.currentBrushSize = minBrushSize; + this.points = []; + this.path = []; + + this.brush = Entities.addEntity({ + type: 'Sphere', + position: { + x: 0, + y: 0, + z: 0 + }, + color: currentColor, + dimensions: { + x: minBrushSize, + y: minBrushSize, + z: minBrushSize + } + }); + + + this.newLine = function(point) { + this.line = Entities.addEntity({ + position: MyAvatar.position, + type: "Line", + color: currentColor, + dimensions: { + x: 10, + y: 10, + z: 10 + }, + lineWidth: 5, + // lifetime: LINE_LIFETIME + }); + this.points = []; + if (point) { + this.points.push(point); + this.path.push(point); + } + this.lines.push(this.line); + } + + this.update = function(deltaTime) { + this.updateControllerState(); + this.avatarPalmOffset = Vec3.subtract(this.palmPosition, MyAvatar.position); + this.projectedForwardDistance = Vec3.dot(Quat.getFront(Camera.getOrientation()), this.avatarPalmOffset); + this.mappedPalmOffset = map(this.projectedForwardDistance, -.5, .5, MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE); + this.tipDirection = Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)); + this.offsetVector = Vec3.multiply(this.mappedPalmOffset, this.tipDirection); + this.drawPoint = Vec3.sum(this.palmPosition, this.offsetVector); + this.currentBrushSize = map(this.triggerValue, 0, 1, minBrushSize, maxBrushSize); + Entities.editEntity(this.brush, { + position: this.drawPoint, + dimensions: { + x: this.currentBrushSize, + y: this.currentBrushSize, + z: this.currentBrushSize + }, + color: currentColor + }); + if (this.triggerValue > MIN_PAINT_TRIGGER_THRESHOLD) { + if (!this.isPainting) { + this.isPainting = true; + this.newLine(); + this.path = []; + } + if (this.strokeCount % STROKE_SMOOTH_FACTOR === 0) { + this.paint(this.drawPoint); + } + this.strokeCount++; + } else if (this.triggerValue < MIN_PAINT_TRIGGER_THRESHOLD && this.isPainting) { + this.releaseTrigger(); + } + + this.oldPalmPosition = this.palmPosition; + this.oldTipPosition = this.tipPosition; + } + + this.releaseTrigger = function() { + this.isPainting = false; + + } + + + this.updateControllerState = function() { + this.undoButtonPressed = Controller.isButtonPressed(this.undoButton); + this.redoButtonPressed = Controller.isButtonPressed(this.redoButton); + this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton); + this.startRideButtonPressed = Controller.isButtonPressed(this.startRideButton); + + //This logic gives us button release + if (this.prevUndoButtonPressed === true && this.undoButtonPressed === false) { + //User released undo button, so undo + this.undoStroke(); + } + if (this.prevRedoButtonPressed === true && this.redoButtonPressed === false) { + this.redoStroke(); + } + + if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { + cycleColor(); + Entities.editEntity(this.brush, { + color: currentColor + }); + } + if (this.prevStartRideButtonPressed === true && this.startRideButtonPressed === false) { + lineRider.toggleRide(); + } + this.prevRedoButtonPressed = this.redoButtonPressed; + this.prevUndoButtonPressed = this.undoButtonPressed; + this.prevCycleColorButtonPressed = this.cycleColorButtonPressed; + this.prevStartRideButtonPressed = this.startRideButtonPressed; + + this.palmPosition = Controller.getSpatialControlPosition(this.palm); + this.tipPosition = Controller.getSpatialControlPosition(this.tip); + this.triggerValue = Controller.getTriggerValue(this.trigger); + } + + this.undoStroke = function() { + var deletedLine = this.lines.pop(); + var deletedLineProps = Entities.getEntityProperties(deletedLine); + this.deletedLines.push(deletedLineProps); + Entities.deleteEntity(deletedLine); + } + + this.redoStroke = function() { + var restoredLine = Entities.addEntity(this.deletedLines.pop()); + Entities.addEntity(restoredLine); + this.lines.push(restoredLine); + } + + this.paint = function(point) { + + currentLineWidth = map(this.triggerValue, 0, 1, minLineWidth, maxLineWidth); + this.points.push(point); + this.path.push(point); + Entities.editEntity(this.line, { + linePoints: this.points, + lineWidth: currentLineWidth, + color: this.rgbColor + }); + if (this.points.length > MAX_POINTS_PER_LINE) { + this.newLine(point); + } + } + + this.cleanup = function() { + Entities.deleteEntity(this.brush); + this.lines.forEach(function(line) { + Entities.deleteEntity(line); + }); + } + } + + function update(deltaTime) { + rightController.update(deltaTime); + leftController.update(deltaTime); + currentTime += deltaTime; + } + + function scriptEnding() { + rightController.cleanup(); + leftController.cleanup(); + lineRider.cleanup(); + } + + function mousePressEvent(event) { + lineRider.mousePressEvent(event); + } + + function vectorIsZero(v) { + return v.x === 0 && v.y === 0 && v.z === 0; + } + + + var rightController = new controller(RIGHT, RIGHT_BUTTON_3, RIGHT_BUTTON_4, RIGHT_BUTTON_1, RIGHT_BUTTON_2); + var leftController = new controller(LEFT, LEFT_BUTTON_3, LEFT_BUTTON_4, LEFT_BUTTON_1, LEFT_BUTTON_2); + + Script.update.connect(update); + Script.scriptEnding.connect(scriptEnding); + Controller.mousePressEvent.connect(mousePressEvent); + + function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); + } + + function randFloat(low, high) { + return Math.floor(low + Math.random() * (high - low)); + } + + + function randInt(low, high) { + return Math.floor(randFloat(low, high)); + } +} \ No newline at end of file From f276ca30cd9c656d690409348d5cab1cbae41559 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 4 Jun 2015 17:55:55 -0700 Subject: [PATCH 14/17] refactoring for common functions. Fix for randFloat --- examples/paint.js | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/examples/paint.js b/examples/paint.js index 9a774a1442..670ffe047c 100644 --- a/examples/paint.js +++ b/examples/paint.js @@ -188,15 +188,6 @@ function MousePaint() { Controller.mouseMoveEvent.connect(mouseMoveEvent); Script.scriptEnding.connect(cleanup); - function randFloat(low, high) { - return Math.floor(low + Math.random() * (high - low)); - } - - - function randInt(low, high) { - return Math.floor(randFloat(low, high)); - } - Controller.keyPressEvent.connect(keyPressEvent); } @@ -420,7 +411,6 @@ function HydraPaint() { Entities.editEntity(this.line, { linePoints: this.points, lineWidth: currentLineWidth, - color: this.rgbColor }); if (this.points.length > MAX_POINTS_PER_LINE) { this.newLine(point); @@ -463,16 +453,17 @@ function HydraPaint() { Script.scriptEnding.connect(scriptEnding); Controller.mousePressEvent.connect(mousePressEvent); - function map(value, min1, max1, min2, max2) { - return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); - } +} function randFloat(low, high) { - return Math.floor(low + Math.random() * (high - low)); + return low + Math.random() * ( high - low ); } function randInt(low, high) { return Math.floor(randFloat(low, high)); } -} \ No newline at end of file + + function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); + } \ No newline at end of file From 9264ef980c4d29d731571478f42481895258dfa9 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 4 Jun 2015 21:13:16 -0700 Subject: [PATCH 15/17] added redo and undo functionality to mouse component of paint script, and fixed overlay not being deleted on script ending bug --- examples/paint.js | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/examples/paint.js b/examples/paint.js index 670ffe047c..787e9156bc 100644 --- a/examples/paint.js +++ b/examples/paint.js @@ -66,11 +66,13 @@ function hydraCheck() { return hydrasConnected; //hydrasConnected; } +//************ Mouse Paint ************************** function MousePaint() { var lines = []; + var deletedLines = []; var isDrawing = false; - var path = []; + var path = []; var lineRider = new LineRider(); lineRider.addStartHandler(function() { @@ -152,6 +154,19 @@ function MousePaint() { } } + function undoStroke() { + var deletedLine = lines.pop(); + var deletedLineProps = Entities.getEntityProperties(deletedLine); + deletedLines.push(deletedLineProps); + Entities.deleteEntity(deletedLine); + } + + function redoStroke() { + var restoredLine = Entities.addEntity(deletedLines.pop()); + Entities.addEntity(restoredLine); + lines.push(restoredLine); + } + function mousePressEvent(event) { lineRider.mousePressEvent(event); path = []; @@ -172,6 +187,12 @@ function MousePaint() { color: currentColor }); } + if (event.text === "z") { + undoStroke(); + } + if(event.text === "x") { + redoStroke(); + } } function cleanup() { @@ -179,6 +200,7 @@ function MousePaint() { Entities.deleteEntity(line); }); Entities.deleteEntity(brush); + lineRider.cleanup(); } @@ -197,7 +219,6 @@ function MousePaint() { - function HydraPaint() { @@ -431,7 +452,7 @@ function HydraPaint() { currentTime += deltaTime; } - function scriptEnding() { + function cleanup() { rightController.cleanup(); leftController.cleanup(); lineRider.cleanup(); @@ -450,20 +471,20 @@ function HydraPaint() { var leftController = new controller(LEFT, LEFT_BUTTON_3, LEFT_BUTTON_4, LEFT_BUTTON_1, LEFT_BUTTON_2); Script.update.connect(update); - Script.scriptEnding.connect(scriptEnding); + Script.scriptEnding.connect(cleanup); Controller.mousePressEvent.connect(mousePressEvent); } - function randFloat(low, high) { - return low + Math.random() * ( high - low ); - } +function randFloat(low, high) { + return low + Math.random() * (high - low); +} - function randInt(low, high) { - return Math.floor(randFloat(low, high)); - } +function randInt(low, high) { + return Math.floor(randFloat(low, high)); +} - function map(value, min1, max1, min2, max2) { - return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); - } \ No newline at end of file +function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} \ No newline at end of file From fd8126510d15d1c38f0e0354c40c5525f6980349 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 4 Jun 2015 21:41:19 -0700 Subject: [PATCH 16/17] marker ball now stays without mouse whether or not user is drawing --- examples/paint.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/paint.js b/examples/paint.js index 787e9156bc..837b217797 100644 --- a/examples/paint.js +++ b/examples/paint.js @@ -131,21 +131,22 @@ function MousePaint() { function mouseMoveEvent(event) { - if (!isDrawing) { - return; - } var pickRay = Camera.computePickRay(event.x, event.y); var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE); var point = Vec3.sum(Camera.getPosition(), addVector); - points.push(point); Entities.editEntity(line, { linePoints: points }); Entities.editEntity(brush, { position: point }); + if (!isDrawing) { + return; + } + + points.push(point); path.push(point); if (points.length === MAX_POINTS_PER_LINE) { @@ -168,6 +169,10 @@ function MousePaint() { } function mousePressEvent(event) { + if(!event.isLeftButton) { + isDrawing = false; + return; + } lineRider.mousePressEvent(event); path = []; newLine(); From 16f55c75572d64f9fc77a87be17619335c5206bc Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 5 Jun 2015 09:52:56 -0700 Subject: [PATCH 17/17] Fix the skybox jiggle in the HMD --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e3c5d735a8..2b6ec63d4b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3314,7 +3314,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb skybox = skyStage->getSkybox(); if (skybox) { gpu::Batch batch; - model::Skybox::render(batch, _viewFrustum, *skybox); + model::Skybox::render(batch, _displayViewFrustum, *skybox); gpu::GLBackend::renderBatch(batch); glUseProgram(0);