From 78b614f8553d956de855ddda35a275ad123350d1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Mar 2015 16:24:32 -0700 Subject: [PATCH 01/26] move avatar details into CharacterController --- libraries/physics/src/CharacterController.cpp | 254 +++++++++++------- libraries/physics/src/CharacterController.h | 24 +- libraries/physics/src/PhysicsEngine.cpp | 125 +++------ libraries/physics/src/PhysicsEngine.h | 3 - 4 files changed, 200 insertions(+), 206 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 5173b368c1..994c95e074 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -16,10 +16,9 @@ subject to the following restrictions: */ -//#include - #include "BulletCollision/CollisionDispatch/btGhostObject.h" +#include "BulletUtil.h" #include "CharacterController.h" @@ -64,8 +63,7 @@ btCollisionObject* m_me; }; */ -class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback -{ +class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: btKinematicClosestNotMeConvexResultCallback(btCollisionObject* me, const btVector3& up, btScalar minSlopeDot) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) @@ -111,16 +109,14 @@ protected: * * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html */ -btVector3 CharacterController::computeReflectionDirection(const btVector3& direction, const btVector3& normal) -{ +btVector3 CharacterController::computeReflectionDirection(const btVector3& direction, const btVector3& normal) { return direction - (btScalar(2.0) * direction.dot(normal)) * normal; } /* * Returns the portion of 'direction' that is parallel to 'normal' */ -btVector3 CharacterController::parallelComponent(const btVector3& direction, const btVector3& normal) -{ +btVector3 CharacterController::parallelComponent(const btVector3& direction, const btVector3& normal) { btScalar magnitude = direction.dot(normal); return normal * magnitude; } @@ -128,36 +124,34 @@ btVector3 CharacterController::parallelComponent(const btVector3& direction, con /* * Returns the portion of 'direction' that is perpindicular to 'normal' */ -btVector3 CharacterController::perpindicularComponent(const btVector3& direction, const btVector3& normal) -{ +btVector3 CharacterController::perpindicularComponent(const btVector3& direction, const btVector3& normal) { return direction - parallelComponent(direction, normal); } -CharacterController::CharacterController( - btPairCachingGhostObject* ghostObject, - btConvexShape* convexShape, - btScalar stepHeight, - int upAxis) { - m_upAxis = upAxis; - m_addedMargin = 0.02; - m_walkDirection.setValue(0,0,0); +CharacterController::CharacterController(AvatarData* avatarData) { + assert(avatarData); + m_avatarData = avatarData; + + createShapeAndGhost(); + + m_upAxis = 1; // HACK: hard coded to be 1 for now (yAxis) + + m_addedMargin = 0.02f; + m_walkDirection.setValue(0.0f,0.0f,0.0f); m_useGhostObjectSweepTest = true; - m_ghostObject = ghostObject; - m_stepHeight = stepHeight; - m_turnAngle = btScalar(0.0); - m_convexShape = convexShape; + m_turnAngle = btScalar(0.0f); m_useWalkDirection = true; // use walk direction by default, legacy behavior - m_velocityTimeInterval = 0.0; - m_verticalVelocity = 0.0; - m_verticalOffset = 0.0; - m_gravity = 9.8 * 3 ; // 3G acceleration. - m_fallSpeed = 55.0; // Terminal velocity of a sky diver in m/s. - m_jumpSpeed = 10.0; // ? + m_velocityTimeInterval = 0.0f; + m_verticalVelocity = 0.0f; + m_verticalOffset = 0.0f; + m_gravity = 9.8f; + m_maxFallSpeed = 55.0f; // Terminal velocity of a sky diver in m/s. + m_jumpSpeed = 10.0f; // ? m_wasOnGround = false; m_wasJumping = false; m_interpolateUp = true; - setMaxSlope(btRadians(45.0)); - m_currentStepOffset = 0; + setMaxSlope(btRadians(45.0f)); + m_currentStepOffset = 0.0f; // internal state data members full_drop = false; @@ -226,8 +220,6 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl } m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2); penetration = true; - } else { - //printf("touching %f\n", dist); } } @@ -237,14 +229,13 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl btTransform newTrans = m_ghostObject->getWorldTransform(); newTrans.setOrigin(m_currentPosition); m_ghostObject->setWorldTransform(newTrans); - //printf("m_touchingNormal = %f,%f,%f\n", m_touchingNormal[0], m_touchingNormal[1], m_touchingNormal[2]); return penetration; } void CharacterController::stepUp( btCollisionWorld* world) { // phase 1: up btTransform start, end; - m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_stepHeight + (m_verticalOffset > 0.f?m_verticalOffset:0.f)); + m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_stepHeight + (m_verticalOffset > 0.0f ? m_verticalOffset : 0.0f)); start.setIdentity(); end.setIdentity(); @@ -301,22 +292,17 @@ void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& //if (tangentMag != 0.0) { if (0) { btVector3 parComponent = parallelDir * btScalar(tangentMag*movementLength); - //printf("parComponent=%f,%f,%f\n", parComponent[0], parComponent[1], parComponent[2]); m_targetPosition += parComponent; } if (normalMag != 0.0) { btVector3 perpComponent = perpindicularDir * btScalar(normalMag*movementLength); - //printf("perpComponent=%f,%f,%f\n", perpComponent[0], perpComponent[1], perpComponent[2]); m_targetPosition += perpComponent; } - } else { - //printf("movementLength don't normalize a zero vector\n"); } } void CharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& walkMove) { - //printf("m_normalizedDirection=%f,%f,%f\n", // m_normalizedDirection[0], m_normalizedDirection[1], m_normalizedDirection[2]); // phase 2: forward and strafe btTransform start, end; @@ -327,7 +313,6 @@ void CharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld btScalar fraction = 1.0; btScalar distance2 = (m_currentPosition-m_targetPosition).length2(); - //printf("distance2=%f\n", distance2); if (m_touchingContact) { if (m_normalizedDirection.dot(m_touchingNormal) > btScalar(0.0)) { @@ -380,7 +365,6 @@ void CharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld break; } } else { - //printf("currentDir: don't normalize a zero vector\n"); break; } } else { @@ -388,7 +372,7 @@ void CharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld m_currentPosition = m_targetPosition; } - //if (callback.m_closestHitFraction == 0.f) { + //if (callback.m_closestHitFraction == 0.0f) { // break; //} @@ -397,24 +381,23 @@ void CharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar dt) { btTransform start, end, end_double; - bool runonce = false; + bool runOnce = false; // phase 3: down /*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0; btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + additionalDownStep); - btScalar downVelocity = (additionalDownStep == 0.0 && m_verticalVelocity<0.0?-m_verticalVelocity:0.0) * dt; - btVector3 gravity_drop = getUpAxisDirections()[m_upAxis] * downVelocity; + btScalar downSpeed = (additionalDownStep == 0.0 && m_verticalVelocity < 0.0 ? -m_verticalVelocity : 0.0); + btVector3 gravity_drop = getUpAxisDirections()[m_upAxis] * downSpeed; m_targetPosition -= (step_drop + gravity_drop);*/ btVector3 orig_position = m_targetPosition; - btScalar downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt; - - if (downVelocity > 0.0 && downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping)) { - downVelocity = m_fallSpeed; + btScalar downSpeed = (m_verticalVelocity < 0.0f) ? -m_verticalVelocity : 0.0f; + if (downSpeed > 0.0f && downSpeed > m_maxFallSpeed && (m_wasOnGround || !m_wasJumping)) { + downSpeed = m_maxFallSpeed; } - btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity); + btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downSpeed * dt); m_targetPosition -= step_drop; btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine); @@ -453,7 +436,7 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d } } - btScalar downVelocity2 = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt; + btScalar downDistance = (m_verticalVelocity < 0.0f ? -m_verticalVelocity : 0.0f) * dt; bool has_hit = false; if(bounce_fix == true) { has_hit = callback.hasHit() || callback2.hasHit(); @@ -461,29 +444,25 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d has_hit = callback2.hasHit(); } - if(downVelocity2 > 0.0 && downVelocity2 < m_stepHeight && has_hit == true && runonce == false + if(downDistance > 0.0 && downDistance < m_stepHeight && has_hit == true && runOnce == false && (m_wasOnGround || !m_wasJumping)) { //redo the velocity calculation when falling a small amount, for fast stairs motion //for larger falls, use the smoother/slower interpolated movement by not touching the target position m_targetPosition = orig_position; - downVelocity = m_stepHeight; - - btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity); + btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + m_stepHeight); m_targetPosition -= step_drop; - runonce = true; + runOnce = true; continue; //re-run previous tests } break; } - if (callback.hasHit() || runonce == true) { + if (callback.hasHit() || runOnce == true) { // we dropped a fraction of the height -> hit floor btScalar fraction = (m_currentPosition.getY() - callback.m_hitPointWorld.getY()) / 2; - //printf("hitpoint: %g - pos %g\n", callback.m_hitPointWorld.getY(), m_currentPosition.getY()); - if (bounce_fix == true) { if (full_drop == true) { m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); @@ -502,39 +481,28 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d m_wasJumping = false; } else { // we dropped the full height - full_drop = true; if (bounce_fix == true) { - downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt; - if (downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping)) { + downSpeed = (m_verticalVelocity < 0.0f) ? -m_verticalVelocity : 0.0f; + if (downSpeed > m_maxFallSpeed && (m_wasOnGround || !m_wasJumping)) { m_targetPosition += step_drop; //undo previous target change - downVelocity = m_fallSpeed; - step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity); + // use fallSpeed instead of downSpeed + step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + m_maxFallSpeed * dt); m_targetPosition -= step_drop; } } - //printf("full drop - %g, %g\n", m_currentPosition.getY(), m_targetPosition.getY()); - m_currentPosition = m_targetPosition; } } - - void CharacterController::setWalkDirection(const btVector3& walkDirection) { m_useWalkDirection = true; m_walkDirection = walkDirection; m_normalizedDirection = getNormalizedVector(m_walkDirection); } - - void CharacterController::setVelocityForTimeInterval(const btVector3& velocity, btScalar timeInterval) { - //printf("setVelocity!\n"); - //printf(" interval: %f\n", timeInterval); - //printf(" velocity: (%f, %f, %f)\n", velocity.x(), velocity.y(), velocity.z()); - m_useWalkDirection = false; m_walkDirection = velocity; m_normalizedDirection = getNormalizedVector(m_walkDirection); @@ -571,23 +539,16 @@ void CharacterController::preStep( btCollisionWorld* collisionWorld) { numPenetrationLoops++; m_touchingContact = true; if (numPenetrationLoops > 4) { - //printf("character could not recover from penetration = %d\n", numPenetrationLoops); break; } } m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); m_targetPosition = m_currentPosition; - //printf("m_targetPosition=%f,%f,%f\n", m_targetPosition[0], m_targetPosition[1], m_targetPosition[2]); } void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt) { - //printf("playerStep(): "); - //printf(" dt = %f", dt); - - // quick check... if (!m_useWalkDirection && m_velocityTimeInterval <= 0.0) { - //printf("\n"); return; // no motion } @@ -595,49 +556,36 @@ void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScala // Update fall velocity. m_verticalVelocity -= m_gravity * dt; - if (m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed) { + if (m_verticalVelocity > m_jumpSpeed) { m_verticalVelocity = m_jumpSpeed; - } - if (m_verticalVelocity < 0.0 && btFabs(m_verticalVelocity) > btFabs(m_fallSpeed)) { - m_verticalVelocity = -btFabs(m_fallSpeed); + } else if (m_verticalVelocity < -m_maxFallSpeed) { + m_verticalVelocity = -m_maxFallSpeed; } m_verticalOffset = m_verticalVelocity * dt; - btTransform xform; xform = m_ghostObject->getWorldTransform(); - //printf("walkDirection(%f,%f,%f)\n", walkDirection[0], walkDirection[1], walkDirection[2]); - //printf("walkSpeed=%f\n", walkSpeed); - stepUp (collisionWorld); if (m_useWalkDirection) { stepForwardAndStrafe(collisionWorld, m_walkDirection); } else { - //printf(" time: %f", m_velocityTimeInterval); - // still have some time left for moving! - btScalar dtMoving = - (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval; + // compute substep and decrement total interval + btScalar dtMoving = (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval; m_velocityTimeInterval -= dt; - // how far will we move while we are moving? + // stepForward substep btVector3 move = m_walkDirection * dtMoving; - - //printf(" dtMoving: %f", dtMoving); - - // okay, step stepForwardAndStrafe(collisionWorld, move); } stepDown(collisionWorld, dt); - //printf("\n"); - xform.setOrigin(m_currentPosition); m_ghostObject->setWorldTransform(xform); } -void CharacterController::setFallSpeed(btScalar fallSpeed) { - m_fallSpeed = fallSpeed; +void CharacterController::setMaxFallSpeed(btScalar speed) { + m_maxFallSpeed = speed; } void CharacterController::setJumpSpeed(btScalar jumpSpeed) { @@ -662,7 +610,7 @@ void CharacterController::jump() { #if 0 currently no jumping. - btTransform xform; + btTransform xform; m_rigidBody->getMotionState()->getWorldTransform(xform); btVector3 up = xform.getBasis()[1]; up.normalize(); @@ -704,3 +652,103 @@ void CharacterController::debugDraw(btIDebugDraw* debugDrawer) { void CharacterController::setUpInterpolate(bool value) { m_interpolateUp = value; } + +// protected +void CharacterController::createShapeAndGhost() { + // get new dimensions from avatar + AABox box = m_avatarData->getLocalAABox(); + const glm::vec3& diagonal = box.getScale(); + float radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); + float halfHeight = 0.5f * diagonal.y - radius; + float MIN_HALF_HEIGHT = 0.1f; + if (halfHeight < MIN_HALF_HEIGHT) { + halfHeight = MIN_HALF_HEIGHT; + } + glm::vec3 offset = box.getCorner() + 0.5f * diagonal; + m_shapeLocalOffset = offset; + + const float MIN_STEP_HEIGHT = 0.35f; + m_stepHeight = glm::max(MIN_STEP_HEIGHT, radius + 0.5f * halfHeight); + + // create new shape + m_convexShape = new btCapsuleShape(radius, 2.0f * halfHeight); + + // create new ghost + m_ghostObject = new btPairCachingGhostObject(); + m_ghostObject->setWorldTransform(btTransform(glmToBullet(m_avatarData->getOrientation()), + glmToBullet(m_avatarData->getPosition()))); + m_ghostObject->setCollisionShape(m_convexShape); + m_ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); +} + +bool CharacterController::needsShapeUpdate() { + // get new dimensions from avatar + AABox box = m_avatarData->getLocalAABox(); + const glm::vec3& diagonal = box.getScale(); + float radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); + float halfHeight = 0.5f * diagonal.y - radius; + float MIN_HALF_HEIGHT = 0.1f; + if (halfHeight < MIN_HALF_HEIGHT) { + halfHeight = MIN_HALF_HEIGHT; + } + glm::vec3 offset = box.getCorner() + 0.5f * diagonal; + + // get old dimensions from shape + btCapsuleShape* capsule = static_cast(m_convexShape); + btScalar oldRadius = capsule->getRadius(); + btScalar oldHalfHeight = capsule->getHalfHeight(); + + // compare dimensions (and offset) + float radiusDelta = glm::abs(radius - oldRadius); + float heightDelta = glm::abs(halfHeight - oldHalfHeight); + if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { + // shape hasn't changed --> nothing to do + float offsetDelta = glm::distance(offset, m_shapeLocalOffset); + if (offsetDelta > FLT_EPSILON) { + // if only the offset changes then we can update it --> no need to rebuild shape + m_shapeLocalOffset = offset; + } + return false; + } + return true; +} + +void CharacterController::updateShape() { + // DANGER: make sure this CharacterController and its GhostShape have been removed from + // the PhysicsEngine before calling this. + + // delete shape and GhostObject + delete m_ghostObject; + m_ghostObject = NULL; + delete m_convexShape; + m_convexShape = NULL; + + createShapeAndGhost(); +} + +void CharacterController::preSimulation(btScalar timeStep) { + if (m_avatarData->isPhysicsEnabled()) { + m_avatarData->lockForRead(); + // update character controller + glm::quat rotation = m_avatarData->getOrientation(); + glm::vec3 position = m_avatarData->getPosition() + rotation * m_shapeLocalOffset; + m_ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); + + btVector3 walkVelocity = glmToBullet(m_avatarData->getVelocity()); + setVelocityForTimeInterval(walkVelocity, timeStep); + m_avatarData->unlock(); + } +} + +void CharacterController::postSimulation() { + if (m_avatarData->isPhysicsEnabled()) { + m_avatarData->lockForWrite(); + const btTransform& avatarTransform = m_ghostObject->getWorldTransform(); + glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); + glm::vec3 offset = rotation * m_shapeLocalOffset; + m_avatarData->setOrientation(rotation); + m_avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); + m_avatarData->unlock(); + } +} + diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 301253b2bd..8e1116f7ea 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -19,6 +19,8 @@ subject to the following restrictions: #ifndef hifi_CharacterController_h #define hifi_CharacterController_h +#include + #include #include #include @@ -39,14 +41,15 @@ ATTRIBUTE_ALIGNED16(class) CharacterController : public btCharacterControllerInt { protected: - btScalar m_halfHeight; - + AvatarData* m_avatarData = NULL; btPairCachingGhostObject* m_ghostObject; + glm::vec3 m_shapeLocalOffset; + btConvexShape* m_convexShape;//is also in m_ghostObject, but it needs to be convex, so we store it here to avoid upcast btScalar m_verticalVelocity; btScalar m_verticalOffset; - btScalar m_fallSpeed; + btScalar m_maxFallSpeed; btScalar m_jumpSpeed; btScalar m_maxJumpHeight; btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value) @@ -95,15 +98,12 @@ protected: void updateTargetPositionBasedOnCollision(const btVector3& hit_normal, btScalar tangentMag = btScalar(0.0), btScalar normalMag = btScalar(1.0)); void stepForwardAndStrafe(btCollisionWorld* collisionWorld, const btVector3& walkMove); void stepDown(btCollisionWorld* collisionWorld, btScalar dt); + void createShapeAndGhost(); public: BT_DECLARE_ALIGNED_ALLOCATOR(); - CharacterController( - btPairCachingGhostObject* ghostObject, - btConvexShape* convexShape, - btScalar stepHeight, - int upAxis = 1); + CharacterController(AvatarData* avatarData); ~CharacterController(); @@ -145,7 +145,7 @@ public: void preStep(btCollisionWorld* collisionWorld); void playerStep(btCollisionWorld* collisionWorld, btScalar dt); - void setFallSpeed(btScalar fallSpeed); + void setMaxFallSpeed(btScalar speed); void setJumpSpeed(btScalar jumpSpeed); void setMaxJumpHeight(btScalar maxJumpHeight); bool canJump() const; @@ -167,6 +167,12 @@ public: bool onGround() const; void setUpInterpolate(bool value); + + bool needsShapeUpdate(); + void updateShape(); + + void preSimulation(btScalar timeStep); + void postSimulation(); }; #endif // hifi_CharacterController_h diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 3e2fabfd89..ba42163a0a 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -24,8 +24,7 @@ uint32_t PhysicsEngine::getNumSubsteps() { } PhysicsEngine::PhysicsEngine(const glm::vec3& offset) - : _originOffset(offset), - _avatarShapeLocalOffset(0.0f) { + : _originOffset(offset) { } PhysicsEngine::~PhysicsEngine() { @@ -277,9 +276,6 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) { } void PhysicsEngine::stepSimulation() { - // expect the engine to have an avatar (and hence: a character controller) - assert(_avatarData); - lock(); // NOTE: the grand order of operations is: // (1) relay incoming changes @@ -296,17 +292,11 @@ void PhysicsEngine::stepSimulation() { _clock.reset(); float timeStep = btMin(dt, MAX_TIMESTEP); - if (_avatarData->isPhysicsEnabled()) { - // update character controller - glm::quat rotation = _avatarData->getOrientation(); - glm::vec3 position = _avatarData->getPosition() + rotation * _avatarShapeLocalOffset; - _avatarGhostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); - - btVector3 walkVelocity = glmToBullet(_avatarData->getVelocity()); - _characterController->setVelocityForTimeInterval(walkVelocity, timeStep); + // This is step (2). + if (_characterController) { + _characterController->preSimulation(timeStep); } - // This is step (2). int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); _numSubsteps += (uint32_t)numSubsteps; stepNonPhysicalKinematics(usecTimestampNow()); @@ -322,20 +312,14 @@ void PhysicsEngine::stepSimulation() { // // TODO: untangle these lock sequences. _entityTree->lockForWrite(); - _avatarData->lockForWrite(); lock(); _dynamicsWorld->synchronizeMotionStates(); - if (_avatarData->isPhysicsEnabled()) { - const btTransform& avatarTransform = _avatarGhostObject->getWorldTransform(); - glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); - glm::vec3 offset = rotation * _avatarShapeLocalOffset; - _avatarData->setOrientation(rotation); - _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); + if (_characterController) { + _characterController->postSimulation(); } unlock(); - _avatarData->unlock(); _entityTree->unlock(); computeCollisionEvents(); @@ -614,76 +598,35 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio return true; } - - void PhysicsEngine::setAvatarData(AvatarData *avatarData) { - assert(avatarData); // don't pass NULL argument - - // compute capsule dimensions - AABox box = avatarData->getLocalAABox(); - const glm::vec3& diagonal = box.getScale(); - float radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - float halfHeight = 0.5f * diagonal.y - radius; - float MIN_HALF_HEIGHT = 0.1f; - if (halfHeight < MIN_HALF_HEIGHT) { - halfHeight = MIN_HALF_HEIGHT; - } - glm::vec3 offset = box.getCorner() + 0.5f * diagonal; - - if (!_avatarData) { - // _avatarData is being initialized - _avatarData = avatarData; - } else { - // _avatarData is being updated - assert(_avatarData == avatarData); - - // get old dimensions from shape - btCapsuleShape* capsule = static_cast(_avatarGhostObject->getCollisionShape()); - btScalar oldRadius = capsule->getRadius(); - btScalar oldHalfHeight = capsule->getHalfHeight(); - - // compare dimensions (and offset) - float radiusDelta = glm::abs(radius - oldRadius); - float heightDelta = glm::abs(halfHeight - oldHalfHeight); - if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { - // shape hasn't changed --> nothing to do - float offsetDelta = glm::distance(offset, _avatarShapeLocalOffset); - if (offsetDelta > FLT_EPSILON) { - // if only the offset changes then we can update it --> no need to rebuild shape - _avatarShapeLocalOffset = offset; - } - return; + if (_characterController) { + bool needsShapeUpdate = _characterController->needsShapeUpdate(); + if (needsShapeUpdate) { + lock(); + // remove old info + _dynamicsWorld->removeCollisionObject(_characterController->getGhostObject()); + _dynamicsWorld->removeAction(_characterController); + // update shape + _characterController->updateShape(); + // insert new info + _dynamicsWorld->addCollisionObject(_characterController->getGhostObject(), + btBroadphaseProxy::CharacterFilter, + btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); + _dynamicsWorld->addAction(_characterController); + _characterController->reset(_dynamicsWorld); + unlock(); } - - // delete old controller and friends - _dynamicsWorld->removeCollisionObject(_avatarGhostObject); - _dynamicsWorld->removeAction(_characterController); - delete _characterController; - _characterController = NULL; - delete _avatarGhostObject; - _avatarGhostObject = NULL; - delete capsule; + } else { + // initialize _characterController + assert(avatarData); // don't pass NULL argument + lock(); + _characterController = new CharacterController(avatarData); + _dynamicsWorld->addCollisionObject(_characterController->getGhostObject(), + btBroadphaseProxy::CharacterFilter, + btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); + _dynamicsWorld->addAction(_characterController); + _characterController->reset(_dynamicsWorld); + unlock(); } - - // set offset - _avatarShapeLocalOffset = offset; - - // build ghost, shape, and controller - _avatarGhostObject = new btPairCachingGhostObject(); - _avatarGhostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), - glmToBullet(_avatarData->getPosition()))); - // ?TODO: use ShapeManager to get avatar's shape? - btCapsuleShape* capsule = new btCapsuleShape(radius, 2.0f * halfHeight); - - _avatarGhostObject->setCollisionShape(capsule); - _avatarGhostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); - - const float MIN_STEP_HEIGHT = 0.35f; - btScalar stepHeight = glm::max(MIN_STEP_HEIGHT, radius + 0.5f * halfHeight); - _characterController = new CharacterController(_avatarGhostObject, capsule, stepHeight); - - _dynamicsWorld->addCollisionObject(_avatarGhostObject, btBroadphaseProxy::CharacterFilter, - btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); - _dynamicsWorld->addAction(_characterController); - _characterController->reset(_dynamicsWorld); } + diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index acf1617b16..cb637c60b9 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -123,9 +123,6 @@ private: /// character collisions CharacterController* _characterController = NULL; - class btPairCachingGhostObject* _avatarGhostObject = NULL; - AvatarData* _avatarData = NULL; - glm::vec3 _avatarShapeLocalOffset; }; #endif // hifi_PhysicsEngine_h From b4263cd2ebf4129b4cd9bd272a63603cfeb2f013 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Mar 2015 17:10:49 -0700 Subject: [PATCH 02/26] allow objs to collide agains char controller but char controller only responds to penetration when enabled --- libraries/physics/src/CharacterController.cpp | 54 ++++++++++++------- libraries/physics/src/CharacterController.h | 5 +- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 994c95e074..8fb3125ad0 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -132,6 +132,11 @@ CharacterController::CharacterController(AvatarData* avatarData) { assert(avatarData); m_avatarData = avatarData; + // cache the "PhysicsEnabled" state of m_avatarData + m_avatarData->lockForRead(); + m_enabled = m_avatarData->isPhysicsEnabled(); + m_avatarData->unlock(); + createShapeAndGhost(); m_upAxis = 1; // HACK: hard coded to be 1 for now (yAxis) @@ -533,6 +538,9 @@ void CharacterController::warp(const btVector3& origin) { void CharacterController::preStep( btCollisionWorld* collisionWorld) { + if (!m_enabled) { + return; + } int numPenetrationLoops = 0; m_touchingContact = false; while (recoverFromPenetration(collisionWorld)) { @@ -548,7 +556,7 @@ void CharacterController::preStep( btCollisionWorld* collisionWorld) { } void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt) { - if (!m_useWalkDirection && m_velocityTimeInterval <= 0.0) { + if (!m_enabled || (!m_useWalkDirection && m_velocityTimeInterval <= 0.0)) { return; // no motion } @@ -637,7 +645,7 @@ btScalar CharacterController::getMaxSlope() const { } bool CharacterController::onGround() const { - return m_verticalVelocity == 0.0 && m_verticalOffset == 0.0; + return m_enabled && m_verticalVelocity == 0.0 && m_verticalOffset == 0.0; } btVector3* CharacterController::getUpAxisDirections() { @@ -656,7 +664,15 @@ void CharacterController::setUpInterpolate(bool value) { // protected void CharacterController::createShapeAndGhost() { // get new dimensions from avatar + m_avatarData->lockForRead(); AABox box = m_avatarData->getLocalAABox(); + + // create new ghost + m_ghostObject = new btPairCachingGhostObject(); + m_ghostObject->setWorldTransform(btTransform(glmToBullet(m_avatarData->getOrientation()), + glmToBullet(m_avatarData->getPosition()))); + m_avatarData->unlock(); + const glm::vec3& diagonal = box.getScale(); float radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); float halfHeight = 0.5f * diagonal.y - radius; @@ -672,18 +688,16 @@ void CharacterController::createShapeAndGhost() { // create new shape m_convexShape = new btCapsuleShape(radius, 2.0f * halfHeight); - - // create new ghost - m_ghostObject = new btPairCachingGhostObject(); - m_ghostObject->setWorldTransform(btTransform(glmToBullet(m_avatarData->getOrientation()), - glmToBullet(m_avatarData->getPosition()))); m_ghostObject->setCollisionShape(m_convexShape); m_ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); } bool CharacterController::needsShapeUpdate() { // get new dimensions from avatar + m_avatarData->lockForRead(); AABox box = m_avatarData->getLocalAABox(); + m_avatarData->unlock(); + const glm::vec3& diagonal = box.getScale(); float radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); float halfHeight = 0.5f * diagonal.y - radius; @@ -727,21 +741,23 @@ void CharacterController::updateShape() { } void CharacterController::preSimulation(btScalar timeStep) { - if (m_avatarData->isPhysicsEnabled()) { - m_avatarData->lockForRead(); - // update character controller - glm::quat rotation = m_avatarData->getOrientation(); - glm::vec3 position = m_avatarData->getPosition() + rotation * m_shapeLocalOffset; - m_ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); - - btVector3 walkVelocity = glmToBullet(m_avatarData->getVelocity()); - setVelocityForTimeInterval(walkVelocity, timeStep); - m_avatarData->unlock(); - } + m_avatarData->lockForRead(); + + // cache the "PhysicsEnabled" state of m_avatarData here + // and use the cached value for the rest of the simulation step + m_enabled = m_avatarData->isPhysicsEnabled(); + + glm::quat rotation = m_avatarData->getOrientation(); + glm::vec3 position = m_avatarData->getPosition() + rotation * m_shapeLocalOffset; + m_ghostObject->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); + btVector3 walkVelocity = glmToBullet(m_avatarData->getVelocity()); + setVelocityForTimeInterval(walkVelocity, timeStep); + + m_avatarData->unlock(); } void CharacterController::postSimulation() { - if (m_avatarData->isPhysicsEnabled()) { + if (m_enabled) { m_avatarData->lockForWrite(); const btTransform& avatarTransform = m_ghostObject->getWorldTransform(); glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 8e1116f7ea..436f9a7277 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -77,8 +77,9 @@ protected: bool m_touchingContact; btVector3 m_touchingNormal; - bool m_wasOnGround; - bool m_wasJumping; + bool m_enabled; + bool m_wasOnGround; + bool m_wasJumping; bool m_useGhostObjectSweepTest; bool m_useWalkDirection; btScalar m_velocityTimeInterval; From 9ea13fac378673cb5982f1158e56af1aaf90d31b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Mar 2015 09:01:36 -0700 Subject: [PATCH 03/26] cleanup formatting --- libraries/physics/src/CharacterController.cpp | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 8fb3125ad0..ae408a9aea 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -53,7 +53,7 @@ m_me = me; virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) { -if(rayResult.m_collisionObject == m_me) +if (rayResult.m_collisionObject == m_me) return 1.0; return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); @@ -221,7 +221,6 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl if (dist < maxPen) { maxPen = dist; m_touchingNormal = pt.m_normalWorldOnB * directionSign;//?? - } m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2); penetration = true; @@ -282,7 +281,7 @@ void CharacterController::stepUp( btCollisionWorld* world) { void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag) { btVector3 movementDirection = m_targetPosition - m_currentPosition; btScalar movementLength = movementDirection.length(); - if (movementLength>SIMD_EPSILON) { + if (movementLength > SIMD_EPSILON) { movementDirection.normalize(); btVector3 reflectDir = computeReflectionDirection(movementDirection, hitNormal); @@ -296,12 +295,12 @@ void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& m_targetPosition = m_currentPosition; //if (tangentMag != 0.0) { if (0) { - btVector3 parComponent = parallelDir * btScalar(tangentMag*movementLength); + btVector3 parComponent = parallelDir * btScalar(tangentMag * movementLength); m_targetPosition += parComponent; } if (normalMag != 0.0) { - btVector3 perpComponent = perpindicularDir * btScalar(normalMag*movementLength); + btVector3 perpComponent = perpindicularDir * btScalar(normalMag * movementLength); m_targetPosition += perpComponent; } } @@ -337,11 +336,9 @@ void CharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; - btScalar margin = m_convexShape->getMargin(); m_convexShape->setMargin(margin + m_addedMargin); - if (m_useGhostObjectSweepTest) { m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); } else { @@ -350,7 +347,6 @@ void CharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld m_convexShape->setMargin(margin); - fraction -= callback.m_closestHitFraction; if (callback.hasHit()) { @@ -443,13 +439,13 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d btScalar downDistance = (m_verticalVelocity < 0.0f ? -m_verticalVelocity : 0.0f) * dt; bool has_hit = false; - if(bounce_fix == true) { + if (bounce_fix == true) { has_hit = callback.hasHit() || callback2.hasHit(); } else { has_hit = callback2.hasHit(); } - if(downDistance > 0.0 && downDistance < m_stepHeight && has_hit == true && runOnce == false + if (downDistance > 0.0 && downDistance < m_stepHeight && has_hit == true && runOnce == false && (m_wasOnGround || !m_wasJumping)) { //redo the velocity calculation when falling a small amount, for fast stairs motion //for larger falls, use the smoother/slower interpolated movement by not touching the target position @@ -475,9 +471,9 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d //due to errors in the closestHitFraction variable when used with large polygons, calculate the hit fraction manually m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, fraction); } - } - else + } else { m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + } full_drop = false; From af789f842dabe3837777ebd85ebadc823d9712c8 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Tue, 17 Mar 2015 22:47:18 +0100 Subject: [PATCH 04/26] #20397 initial version of script --- examples/example/entities/makeHouses.js | 146 ++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 examples/example/entities/makeHouses.js diff --git a/examples/example/entities/makeHouses.js b/examples/example/entities/makeHouses.js new file mode 100644 index 0000000000..26224fd58a --- /dev/null +++ b/examples/example/entities/makeHouses.js @@ -0,0 +1,146 @@ +// +// makeHouses.js +// +// +// Created by Stojce Slavkovski on March 14, 2015 +// Copyright 2015 High Fidelity, Inc. +// +// This sample script that creates house entities based on parameters. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function () { + + /** options **/ + var numHouses = 100; + var xRange = 300; + var yRange = 300; + + var sizeOfTheHouse = { + x: 10, + y: 10, + z: 10 + }; + /**/ + + var modelUrl = "http://localhost/~stojce/models/3-Buildings-2-SanFranciscoHouse-"; + var modelurlExt = ".fbx"; + var modelVariations = 90; + var houses = []; + + function addHouseAt(position, rotation) { + // get random house model + var modelNumber = 1 + Math.floor(Math.random() * (modelVariations - 1)); + var modUrl = modelUrl + modelNumber + modelurlExt; + print("Model ID:" + modelNumber); + + var properties = { + type: "Model", + position: position, + rotation: rotation, + dimensions: sizeOfTheHouse, + modelURL: modUrl + }; + + return Entities.addEntity(properties); + } + + // calculate initial position + var posX = MyAvatar.position.x - (xRange / 2); + var measures = calculateParcels(numHouses, xRange, yRange); + var dd = 0; + + // avatar facing rotation + var rotEven = Quat.fromPitchYawRollDegrees(0, 270.0 + MyAvatar.bodyYaw, 0.0); + + // avatar opposite rotation + var rotOdd = Quat.fromPitchYawRollDegrees(0, 90.0 + MyAvatar.bodyYaw, 0.0); + var housePos = Vec3.sum(MyAvatar.position, Quat.getFront(Camera.getOrientation())); + + for (var j = 0; j < measures.rows; j++) { + + var posX1 = 0 - (xRange / 2); + dd += measures.parcelLength; + + for (var i = 0; i < measures.cols; i++) { + + // skip reminder of houses + if (houses.length > numHouses) { + break; + } + + var posShift = { + x: posX1, + y: 0, + z: dd + }; + + print("House nr.:" + houses.length + 1); + houses.push( + addHouseAt(Vec3.sum(housePos, posShift), (j % 2 == 0) ? rotEven : rotOdd) + ); + posX1 += measures.parcelWidth; + } + } + + // calculate rows and columns in area, and dimension of single parcel + function calculateParcels(items, areaWidth, areaLength) { + + var idealSize = Math.min(Math.sqrt(areaWidth * areaLength / items), areaWidth, areaLength); + + var baseWidth = Math.min(Math.floor(areaWidth / idealSize), items); + var baseLength = Math.min(Math.floor(areaLength / idealSize), items); + + var sirRows = baseWidth; + var sirCols = Math.ceil(items / sirRows); + var sirW = areaWidth / sirRows; + var sirL = areaLength / sirCols; + + var visCols = baseLength; + var visRows = Math.ceil(items / visCols); + var visW = areaWidth / visRows; + var visL = areaLength / visCols; + + var rows = 0; + var cols = 0; + var parcelWidth = 0; + var parcelLength = 0; + + if (Math.min(sirW, sirL) > Math.min(visW, visL)) { + rows = sirRows; + cols = sirCols; + parcelWidth = sirW; + parcelLength = sirL; + } else { + rows = visRows; + cols = visCols; + parcelWidth = visW; + parcelLength = visL; + } + + print("rows:" + rows); + print("cols:" + cols); + print("parcelWidth:" + parcelWidth); + print("parcelLength:" + parcelLength); + + return { + rows: rows, + cols: cols, + parcelWidth: parcelWidth, + parcelLength: parcelLength + }; + } + + function cleanup() { + while (houses.length > 0) { + if (!houses[0].isKnownID) { + houses[0] = Entities.identifyEntity(houses[0]); + } + Entities.deleteEntity(houses.shift()); + } + } + + Script.scriptEnding.connect(cleanup); +})(); \ No newline at end of file From 8eec83c14489008c8c5de35de14d18bab08bccf1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Mar 2015 22:28:38 -0700 Subject: [PATCH 05/26] comments and formatting --- libraries/physics/src/CharacterController.cpp | 17 ++++++++++++----- libraries/physics/src/CharacterController.h | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index ae408a9aea..6fdf55abed 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -90,6 +90,9 @@ class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::Clo hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; } + // Note: hitNormalWorld points into character, away from object + // and m_up points opposite to movement + btScalar dotUp = m_up.dot(hitNormalWorld); if (dotUp < m_minSlopeDot) { return btScalar(1.0); @@ -209,10 +212,10 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray); } - for (int j=0;jgetBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0); - for (int p=0;pgetNumContacts();p++) { + btScalar directionSign = (manifold->getBody0() == m_ghostObject) ? btScalar(-1.0) : btScalar(1.0); + for (int p = 0;p < manifold->getNumContacts(); p++) { const btManifoldPoint&pt = manifold->getContactPoint(p); btScalar dist = pt.getDistance(); @@ -570,6 +573,11 @@ void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScala btTransform xform; xform = m_ghostObject->getWorldTransform(); + // the algorithm is as follows: + // (1) step the character up a little bit so that its forward step doesn't hit the floor + // (2) step the character forward + // (3) step the character down so that its back in contact with the ground + stepUp (collisionWorld); if (m_useWalkDirection) { stepForwardAndStrafe(collisionWorld, m_walkDirection); @@ -679,8 +687,7 @@ void CharacterController::createShapeAndGhost() { glm::vec3 offset = box.getCorner() + 0.5f * diagonal; m_shapeLocalOffset = offset; - const float MIN_STEP_HEIGHT = 0.35f; - m_stepHeight = glm::max(MIN_STEP_HEIGHT, radius + 0.5f * halfHeight); + m_stepHeight = 0.1f; // create new shape m_convexShape = new btCapsuleShape(radius, 2.0f * halfHeight); diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 436f9a7277..1805dcba74 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -58,7 +58,7 @@ protected: btScalar m_turnAngle; - btScalar m_stepHeight; + btScalar m_stepHeight; // height of stepUp prior to stepForward btScalar m_addedMargin;//@todo: remove this and fix the code @@ -75,7 +75,7 @@ protected: btManifoldArray m_manifoldArray; bool m_touchingContact; - btVector3 m_touchingNormal; + btVector3 m_touchingNormal; // points from character to object bool m_enabled; bool m_wasOnGround; From 00f1d96af5060180a375a8cbf7b9deb35cbeeb79 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Wed, 18 Mar 2015 19:30:08 +0100 Subject: [PATCH 06/26] Use existing house models --- examples/example/entities/makeHouses.js | 33 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/examples/example/entities/makeHouses.js b/examples/example/entities/makeHouses.js index 26224fd58a..12205db4ce 100644 --- a/examples/example/entities/makeHouses.js +++ b/examples/example/entities/makeHouses.js @@ -25,23 +25,38 @@ }; /**/ - var modelUrl = "http://localhost/~stojce/models/3-Buildings-2-SanFranciscoHouse-"; - var modelurlExt = ".fbx"; - var modelVariations = 90; + // + // var modelUrl = "http://localhost/~stojce/models/3-Buildings-2-SanFranciscoHouse-"; + // var modelurlExt = ".fbx"; + // var modelVariations = 100; + + var houseModels = [ + "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseBlue.fbx", + "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseBlue3.fbx", + "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseGreen.fbx", + "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseGreen2.fbx", + "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseRed.fbx", + "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseRose.fbx", + "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseViolet.fbx", + "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseYellow.fbx", + "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseYellow2.fbx" + ]; + var houses = []; function addHouseAt(position, rotation) { // get random house model - var modelNumber = 1 + Math.floor(Math.random() * (modelVariations - 1)); - var modUrl = modelUrl + modelNumber + modelurlExt; - print("Model ID:" + modelNumber); - + //var modelNumber = 1 + Math.floor(Math.random() * (modelVariations - 1)); + //var modelUrl = modelUrl + modelNumber + modelurlExt; + //print("Model ID:" + modelNumber); + var modelUrl = houseModels[Math.floor(Math.random() * houseModels.length)]; + var properties = { type: "Model", position: position, rotation: rotation, dimensions: sizeOfTheHouse, - modelURL: modUrl + modelURL: modelUrl }; return Entities.addEntity(properties); @@ -77,7 +92,7 @@ z: dd }; - print("House nr.:" + houses.length + 1); + print("House nr.:" + (houses.length + 1)); houses.push( addHouseAt(Vec3.sum(housePos, posShift), (j % 2 == 0) ? rotEven : rotOdd) ); From 57011c2d4bd1fc0f423b5069460ac2e003653f42 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Thu, 19 Mar 2015 20:55:18 +0100 Subject: [PATCH 07/26] get models from S3 --- examples/example/entities/makeHouses.js | 46 +++++++++++-------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/examples/example/entities/makeHouses.js b/examples/example/entities/makeHouses.js index 12205db4ce..37bc1d5a8e 100644 --- a/examples/example/entities/makeHouses.js +++ b/examples/example/entities/makeHouses.js @@ -20,36 +20,32 @@ var sizeOfTheHouse = { x: 10, - y: 10, + y: 15, z: 10 }; - /**/ - - // - // var modelUrl = "http://localhost/~stojce/models/3-Buildings-2-SanFranciscoHouse-"; - // var modelurlExt = ".fbx"; - // var modelVariations = 100; - var houseModels = [ - "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseBlue.fbx", - "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseBlue3.fbx", - "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseGreen.fbx", - "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseGreen2.fbx", - "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseRed.fbx", - "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseRose.fbx", - "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseViolet.fbx", - "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseYellow.fbx", - "http://public.highfidelity.io/models/entities/3-Buildings-2-SanFranciscoHouseYellow2.fbx" - ]; - + var randomizeModels = false; + /**/ + + var modelUrlPrefix = "http://public.highfidelity.io/load_testing/3-Buildings-2-SanFranciscoHouse-"; + var modelurlExt = ".fbx"; + var modelVariations = 100; + var houses = []; function addHouseAt(position, rotation) { - // get random house model - //var modelNumber = 1 + Math.floor(Math.random() * (modelVariations - 1)); - //var modelUrl = modelUrl + modelNumber + modelurlExt; - //print("Model ID:" + modelNumber); - var modelUrl = houseModels[Math.floor(Math.random() * houseModels.length)]; + // get house model + var modelNumber = randomizeModels ? + 1 + Math.floor(Math.random() * (modelVariations - 1)) : + (houses.length + 1) % modelVariations; + + if (modelNumber == 0) { + modelNumber = modelVariations; + } + + var modelUrl = modelUrlPrefix + (modelNumber + "") + modelurlExt; + print("Model ID:" + modelNumber); + print("Model URL:" + modelUrl); var properties = { type: "Model", @@ -158,4 +154,4 @@ } Script.scriptEnding.connect(cleanup); -})(); \ No newline at end of file +})(); From 3cd2ce82d4238e39b88f132cda19738e31028a93 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Mar 2015 15:31:34 -0700 Subject: [PATCH 08/26] tuning character so it can walk up ledges --- libraries/physics/src/CharacterController.cpp | 424 ++++++++++-------- libraries/physics/src/CharacterController.h | 14 +- 2 files changed, 241 insertions(+), 197 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 6fdf55abed..2c01e2ae32 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -107,6 +107,91 @@ protected: btScalar m_minSlopeDot; }; +class StepDownConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { + // special convex sweep callback for character during the stepDown() phase + public: + StepDownConvexResultCallback(btCollisionObject* me, + const btVector3& up, + btScalar minSlopeDot, + const btVector3& start, + const btVector3& step, + btScalar radius, + btScalar halfHeight, + btVector3 pushDirection) + : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) + , m_me(me) + , m_up(up) + , m_minSlopeDot(minSlopeDot) + , m_start(start) + , m_step(step) + , m_radius(radius) + , m_halfHeight(halfHeight) + , m_pushDirection(pushDirection) + { + } + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace) { + if (convexResult.m_hitCollisionObject == m_me) { + return btScalar(1.0); + } + + if (!convexResult.m_hitCollisionObject->hasContactResponse()) { + return btScalar(1.0); + } + + btVector3 hitNormalWorld; + if (normalInWorldSpace) { + hitNormalWorld = convexResult.m_hitNormalLocal; + } else { + ///need to transform normal into worldspace + hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis() * convexResult.m_hitNormalLocal; + } + + // Note: hitNormalWorld points into character, away from object + // and m_up points opposite to movement + + btScalar dotUp = m_up.dot(hitNormalWorld); + if (dotUp < m_minSlopeDot) { + if (hitNormalWorld.dot(m_pushDirection) > 0.0f) { + // ignore hits that push in same direction as character is moving + // which helps character NOT snag when stepping off ledges + return btScalar(1.0f); + } + + // compute the angle between "down" and the line from character center to "hit" point + btVector3 fractionalStep = convexResult.m_hitFraction * m_step; + btVector3 localHit = convexResult.m_hitPointLocal - m_start + fractionalStep; + btScalar angle = localHit.angle(-m_up); + + // compute a maxAngle based on size of m_step + btVector3 side(m_radius, - (m_halfHeight - m_step.length() + fractionalStep.dot(m_up)), 0.0f); + btScalar maxAngle = side.angle(-m_up); + + // Ignore hits that are larger than maxAngle. Effectively what is happening here is: + // we're ignoring hits at contacts that have non-vertical normals... if they hit higher + // than the character's "feet". Ignoring the contact allows the character to slide down + // for these hits. In other words, vertical walls against the character's torso will + // not prevent them from "stepping down" to find the floor. + if (angle > maxAngle) { + return btScalar(1.0f); + } + } + + btScalar fraction = ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace); + return fraction; + } + +protected: + btCollisionObject* m_me; + const btVector3 m_up; + btScalar m_minSlopeDot; + btVector3 m_start; + btVector3 m_step; + btScalar m_radius; + btScalar m_halfHeight; + btVector3 m_pushDirection; +}; + /* * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal' * @@ -146,7 +231,6 @@ CharacterController::CharacterController(AvatarData* avatarData) { m_addedMargin = 0.02f; m_walkDirection.setValue(0.0f,0.0f,0.0f); - m_useGhostObjectSweepTest = true; m_turnAngle = btScalar(0.0f); m_useWalkDirection = true; // use walk direction by default, legacy behavior m_velocityTimeInterval = 0.0f; @@ -159,7 +243,7 @@ CharacterController::CharacterController(AvatarData* avatarData) { m_wasJumping = false; m_interpolateUp = true; setMaxSlope(btRadians(45.0f)); - m_currentStepOffset = 0.0f; + m_lastStepUp = 0.0f; // internal state data members full_drop = false; @@ -194,6 +278,9 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher()); m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); + btVector3 up = getUpAxisDirections()[m_upAxis]; + + btVector3 currentPosition = m_currentPosition; btScalar maxPen = btScalar(0.0); for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++) { @@ -214,23 +301,52 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl for (int j = 0;j < m_manifoldArray.size(); j++) { btPersistentManifold* manifold = m_manifoldArray[j]; - btScalar directionSign = (manifold->getBody0() == m_ghostObject) ? btScalar(-1.0) : btScalar(1.0); + btScalar directionSign = (manifold->getBody0() == m_ghostObject) ? btScalar(1.0) : btScalar(-1.0); for (int p = 0;p < manifold->getNumContacts(); p++) { const btManifoldPoint&pt = manifold->getContactPoint(p); btScalar dist = pt.getDistance(); if (dist < 0.0) { - if (dist < maxPen) { - maxPen = dist; - m_touchingNormal = pt.m_normalWorldOnB * directionSign;//?? + bool useContact = true; + btVector3 normal = pt.m_normalWorldOnB; + normal *= directionSign; // always points from object to character + + btScalar normalDotUp = normal.dot(up); + if (normalDotUp < m_maxSlopeCosine) { + // this contact has a non-vertical normal... might need to ignored + btVector3 collisionPoint; + if (directionSign > 0.0) { + collisionPoint = pt.getPositionWorldOnB(); + } else { + collisionPoint = pt.getPositionWorldOnA(); + } + + // we do math in frame where character base is origin + btVector3 characterBase = currentPosition - (m_radius + m_halfHeight) * up; + collisionPoint -= characterBase; + btScalar collisionHeight = collisionPoint.dot(up); + + if (collisionHeight < m_lastStepUp) { + // This contact is below the lastStepUp, so we ignore it for penetration resolution, + // otherwise it may prevent the character from getting close enough to find any available + // horizontal foothold that would allow it to climbe the ledge. In other words, we're + // making the character's "feet" soft for collisions against steps, but not floors. + useContact = false; + } + } + if (useContact) { + + if (dist < maxPen) { + maxPen = dist; + m_floorNormal = normal; + } + const btScalar INCREMENTAL_RESOLUTION_FACTOR = 0.2f; + m_currentPosition += normal * (fabsf(dist) * INCREMENTAL_RESOLUTION_FACTOR); + penetration = true; } - m_currentPosition += pt.m_normalWorldOnB * directionSign * dist * btScalar(0.2); - penetration = true; } } - - //manifold->clearManifold(); } } btTransform newTrans = m_ghostObject->getWorldTransform(); @@ -241,43 +357,44 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl void CharacterController::stepUp( btCollisionWorld* world) { // phase 1: up + + // compute start and end btTransform start, end; - m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_stepHeight + (m_verticalOffset > 0.0f ? m_verticalOffset : 0.0f)); - start.setIdentity(); - end.setIdentity(); - - /* FIXME: Handle penetration properly */ start.setOrigin(m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_convexShape->getMargin() + m_addedMargin)); + + //m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * (m_stepHeight + (m_verticalOffset > 0.0f ? m_verticalOffset : 0.0f)); + m_targetPosition = m_currentPosition + getUpAxisDirections()[m_upAxis] * m_stepHeight; + end.setIdentity(); end.setOrigin(m_targetPosition); - btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, -getUpAxisDirections()[m_upAxis], btScalar(0.7071)); + // sweep up + btVector3 sweepDirNegative = -getUpAxisDirections()[m_upAxis]; + btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, sweepDirNegative, btScalar(0.7071)); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; - - if (m_useGhostObjectSweepTest) { - m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration); - } - else { - world->convexSweepTest(m_convexShape, start, end, callback); - } + m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration); if (callback.hasHit()) { + // we hit something, so zero our vertical velocity + m_verticalVelocity = 0.0; + m_verticalOffset = 0.0; + // Only modify the position if the hit was a slope and not a wall or ceiling. if (callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > 0.0) { - // we moved up only a fraction of the step height - m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction; + m_lastStepUp = m_stepHeight * callback.m_closestHitFraction; if (m_interpolateUp == true) { m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); } else { m_currentPosition = m_targetPosition; } + } else { + m_lastStepUp = m_stepHeight; + m_currentPosition = m_targetPosition; } - m_verticalVelocity = 0.0; - m_verticalOffset = 0.0; } else { - m_currentStepOffset = m_stepHeight; m_currentPosition = m_targetPosition; + m_lastStepUp = m_stepHeight; } } @@ -309,194 +426,124 @@ void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& } } -void CharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& walkMove) { - // m_normalizedDirection[0], m_normalizedDirection[1], m_normalizedDirection[2]); - // phase 2: forward and strafe - btTransform start, end; - m_targetPosition = m_currentPosition + walkMove; +void CharacterController::stepForward( btCollisionWorld* collisionWorld, const btVector3& movement) { + // phase 2: forward + m_targetPosition = m_currentPosition + movement; + btTransform start, end; start.setIdentity(); end.setIdentity(); - btScalar fraction = 1.0; - btScalar distance2 = (m_currentPosition-m_targetPosition).length2(); - + /* TODO: experiment with this to see if we can use this to help direct motion when a floor is available if (m_touchingContact) { - if (m_normalizedDirection.dot(m_touchingNormal) > btScalar(0.0)) { - //interferes with step movement - //updateTargetPositionBasedOnCollision(m_touchingNormal); + if (m_normalizedDirection.dot(m_floorNormal) < btScalar(0.0)) { + updateTargetPositionBasedOnCollision(m_floorNormal, 1.0f, 1.0f); } - } + }*/ + // modify shape's margin for the sweeps + btScalar margin = m_convexShape->getMargin(); + m_convexShape->setMargin(margin + m_addedMargin); + + const btScalar MIN_STEP_DISTANCE = 0.0001f; + btVector3 step = m_targetPosition - m_currentPosition; + btScalar stepLength2 = step.length2(); int maxIter = 10; - while (fraction > btScalar(0.01) && maxIter-- > 0) { + while (stepLength2 > MIN_STEP_DISTANCE && maxIter-- > 0) { start.setOrigin(m_currentPosition); end.setOrigin(m_targetPosition); - btVector3 sweepDirNegative(m_currentPosition - m_targetPosition); + // sweep forward + btVector3 sweepDirNegative(m_currentPosition - m_targetPosition); btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, sweepDirNegative, btScalar(0.0)); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; - - btScalar margin = m_convexShape->getMargin(); - m_convexShape->setMargin(margin + m_addedMargin); - - if (m_useGhostObjectSweepTest) { - m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); - } else { - collisionWorld->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); - } - - m_convexShape->setMargin(margin); - - fraction -= callback.m_closestHitFraction; + m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); if (callback.hasHit()) { - // we moved only a fraction - //btScalar hitDistance; - //hitDistance = (callback.m_hitPointWorld - m_currentPosition).length(); + // we hit soemthing! + // Compute new target position by removing portion cut-off by collision, which will produce a new target + // that is the closest approach of the the obstacle plane to the original target. + step = m_targetPosition - m_currentPosition; + btScalar stepDotNormal = step.dot(callback.m_hitNormalWorld); // we expect this dot to be negative + step += (stepDotNormal * (1.0f - callback.m_closestHitFraction)) * callback.m_hitNormalWorld; + m_targetPosition = m_currentPosition + step; - //m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); - - updateTargetPositionBasedOnCollision(callback.m_hitNormalWorld); - btVector3 currentDir = m_targetPosition - m_currentPosition; - distance2 = currentDir.length2(); - if (distance2 > SIMD_EPSILON) { - currentDir.normalize(); - /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */ - if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0)) { - break; - } - } else { - break; - } + stepLength2 = step.length2(); } else { - // we moved whole way + // we swept to the end without hitting anything m_currentPosition = m_targetPosition; + break; } - - //if (callback.m_closestHitFraction == 0.0f) { - // break; - //} - } + + // restore shape's margin + m_convexShape->setMargin(margin); } void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar dt) { - btTransform start, end, end_double; - bool runOnce = false; - // phase 3: down - /*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0; - btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + additionalDownStep); - btScalar downSpeed = (additionalDownStep == 0.0 && m_verticalVelocity < 0.0 ? -m_verticalVelocity : 0.0); - btVector3 gravity_drop = getUpAxisDirections()[m_upAxis] * downSpeed; - m_targetPosition -= (step_drop + gravity_drop);*/ - - btVector3 orig_position = m_targetPosition; + // + // The "stepDown" phase first makes a normal sweep down that cancels the lift from the "stepUp" phase. + // If it hits a ledge then it stops otherwise it makes another sweep down in search of a floor within + // reach of the character's feet. btScalar downSpeed = (m_verticalVelocity < 0.0f) ? -m_verticalVelocity : 0.0f; if (downSpeed > 0.0f && downSpeed > m_maxFallSpeed && (m_wasOnGround || !m_wasJumping)) { downSpeed = m_maxFallSpeed; } - btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downSpeed * dt); - m_targetPosition -= step_drop; + // first sweep for ledge + btVector3 step = getUpAxisDirections()[m_upAxis] * (-(m_lastStepUp + downSpeed * dt)); - btKinematicClosestNotMeConvexResultCallback callback(m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine); + StepDownConvexResultCallback callback(m_ghostObject, + getUpAxisDirections()[m_upAxis], + m_maxSlopeCosine, m_currentPosition, + step, m_radius, m_halfHeight, m_walkDirection); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; - btKinematicClosestNotMeConvexResultCallback callback2 (m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine); - callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; - callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; + btTransform start, end; + start.setIdentity(); + end.setIdentity(); - while (1) { - start.setIdentity(); - end.setIdentity(); + start.setOrigin(m_currentPosition); + m_targetPosition = m_currentPosition + step; + end.setOrigin(m_targetPosition); + m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); - end_double.setIdentity(); + if (callback.hasHit()) { + m_currentPosition += callback.m_closestHitFraction * step; + m_verticalVelocity = 0.0f; + m_verticalOffset = 0.0f; + m_wasJumping = false; + } else { + // sweep again for floor within downStep threshold + StepDownConvexResultCallback callback2 (m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine, m_currentPosition, step, m_radius, m_halfHeight, m_walkDirection); + + callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; + callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; + + m_currentPosition = m_targetPosition; + btVector3 oldPosition = m_currentPosition; + step = (- m_stepHeight) * getUpAxisDirections()[m_upAxis]; + m_targetPosition = m_currentPosition + step; start.setOrigin(m_currentPosition); end.setOrigin(m_targetPosition); + m_ghostObject->convexSweepTest(m_convexShape, start, end, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); - //set double test for 2x the step drop, to check for a large drop vs small drop - end_double.setOrigin(m_targetPosition - step_drop); - - if (m_useGhostObjectSweepTest) { - m_ghostObject->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); - - if (!callback.hasHit()) { - //test a double fall height, to see if the character should interpolate it's fall (full) or not (partial) - m_ghostObject->convexSweepTest(m_convexShape, start, end_double, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); - } + if (callback2.hasHit()) { + m_currentPosition += callback2.m_closestHitFraction * step; + m_verticalVelocity = 0.0f; + m_verticalOffset = 0.0f; + m_wasJumping = false; } else { - collisionWorld->convexSweepTest(m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); - - if (!callback.hasHit()) { - //test a double fall height, to see if the character should interpolate it's fall (large) or not (small) - collisionWorld->convexSweepTest(m_convexShape, start, end_double, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); - } + // nothing to step down on, so remove the stepUp effect + m_currentPosition = oldPosition - m_lastStepUp * getUpAxisDirections()[m_upAxis]; + m_lastStepUp = 0.0f; } - - btScalar downDistance = (m_verticalVelocity < 0.0f ? -m_verticalVelocity : 0.0f) * dt; - bool has_hit = false; - if (bounce_fix == true) { - has_hit = callback.hasHit() || callback2.hasHit(); - } else { - has_hit = callback2.hasHit(); - } - - if (downDistance > 0.0 && downDistance < m_stepHeight && has_hit == true && runOnce == false - && (m_wasOnGround || !m_wasJumping)) { - //redo the velocity calculation when falling a small amount, for fast stairs motion - //for larger falls, use the smoother/slower interpolated movement by not touching the target position - - m_targetPosition = orig_position; - btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + m_stepHeight); - m_targetPosition -= step_drop; - runOnce = true; - continue; //re-run previous tests - } - break; - } - - if (callback.hasHit() || runOnce == true) { - // we dropped a fraction of the height -> hit floor - - btScalar fraction = (m_currentPosition.getY() - callback.m_hitPointWorld.getY()) / 2; - - if (bounce_fix == true) { - if (full_drop == true) { - m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); - } else { - //due to errors in the closestHitFraction variable when used with large polygons, calculate the hit fraction manually - m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, fraction); - } - } else { - m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); - } - - full_drop = false; - - m_verticalVelocity = 0.0; - m_verticalOffset = 0.0; - m_wasJumping = false; - } else { - // we dropped the full height - full_drop = true; - - if (bounce_fix == true) { - downSpeed = (m_verticalVelocity < 0.0f) ? -m_verticalVelocity : 0.0f; - if (downSpeed > m_maxFallSpeed && (m_wasOnGround || !m_wasJumping)) { - m_targetPosition += step_drop; //undo previous target change - // use fallSpeed instead of downSpeed - step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + m_maxFallSpeed * dt); - m_targetPosition -= step_drop; - } - } - m_currentPosition = m_targetPosition; } } @@ -576,11 +623,11 @@ void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScala // the algorithm is as follows: // (1) step the character up a little bit so that its forward step doesn't hit the floor // (2) step the character forward - // (3) step the character down so that its back in contact with the ground + // (3) step the character down looking for new ledges, the original floor, or a floor one step below where we started - stepUp (collisionWorld); + stepUp(collisionWorld); if (m_useWalkDirection) { - stepForwardAndStrafe(collisionWorld, m_walkDirection); + stepForward(collisionWorld, m_walkDirection); } else { // compute substep and decrement total interval btScalar dtMoving = (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval; @@ -588,7 +635,7 @@ void CharacterController::playerStep( btCollisionWorld* collisionWorld, btScala // stepForward substep btVector3 move = m_walkDirection * dtMoving; - stepForwardAndStrafe(collisionWorld, move); + stepForward(collisionWorld, move); } stepDown(collisionWorld, dt); @@ -678,19 +725,23 @@ void CharacterController::createShapeAndGhost() { m_avatarData->unlock(); const glm::vec3& diagonal = box.getScale(); - float radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - float halfHeight = 0.5f * diagonal.y - radius; + m_radius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); + m_halfHeight = 0.5f * diagonal.y - m_radius; float MIN_HALF_HEIGHT = 0.1f; - if (halfHeight < MIN_HALF_HEIGHT) { - halfHeight = MIN_HALF_HEIGHT; + if (m_halfHeight < MIN_HALF_HEIGHT) { + m_halfHeight = MIN_HALF_HEIGHT; } glm::vec3 offset = box.getCorner() + 0.5f * diagonal; m_shapeLocalOffset = offset; - m_stepHeight = 0.1f; + // stepHeight affects the heights of ledges that the character can ascend + // however the actual ledge height is some function of m_stepHeight + // due to character shape and this CharacterController algorithm + // (the function is approximately 2*m_stepHeight) + m_stepHeight = 0.1f * (m_radius + m_halfHeight) + 0.1f; // create new shape - m_convexShape = new btCapsuleShape(radius, 2.0f * halfHeight); + m_convexShape = new btCapsuleShape(m_radius, 2.0f * m_halfHeight); m_ghostObject->setCollisionShape(m_convexShape); m_ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); } @@ -710,14 +761,9 @@ bool CharacterController::needsShapeUpdate() { } glm::vec3 offset = box.getCorner() + 0.5f * diagonal; - // get old dimensions from shape - btCapsuleShape* capsule = static_cast(m_convexShape); - btScalar oldRadius = capsule->getRadius(); - btScalar oldHalfHeight = capsule->getHalfHeight(); - // compare dimensions (and offset) - float radiusDelta = glm::abs(radius - oldRadius); - float heightDelta = glm::abs(halfHeight - oldHalfHeight); + float radiusDelta = glm::abs(radius - m_radius); + float heightDelta = glm::abs(halfHeight - m_halfHeight); if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { // shape hasn't changed --> nothing to do float offsetDelta = glm::distance(offset, m_shapeLocalOffset); diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 1805dcba74..7969fffd9d 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -46,9 +46,11 @@ protected: glm::vec3 m_shapeLocalOffset; btConvexShape* m_convexShape;//is also in m_ghostObject, but it needs to be convex, so we store it here to avoid upcast + btScalar m_radius; + btScalar m_halfHeight; btScalar m_verticalVelocity; - btScalar m_verticalOffset; + btScalar m_verticalOffset; // fall distance from velocity this frame btScalar m_maxFallSpeed; btScalar m_jumpSpeed; btScalar m_maxJumpHeight; @@ -68,19 +70,18 @@ protected: //some internal variables btVector3 m_currentPosition; - btScalar m_currentStepOffset; btVector3 m_targetPosition; + btScalar m_lastStepUp; ///keep track of the contact manifolds btManifoldArray m_manifoldArray; bool m_touchingContact; - btVector3 m_touchingNormal; // points from character to object + btVector3 m_floorNormal; // points from object to character bool m_enabled; bool m_wasOnGround; bool m_wasJumping; - bool m_useGhostObjectSweepTest; bool m_useWalkDirection; btScalar m_velocityTimeInterval; int m_upAxis; @@ -97,7 +98,7 @@ protected: bool recoverFromPenetration(btCollisionWorld* collisionWorld); void stepUp(btCollisionWorld* collisionWorld); void updateTargetPositionBasedOnCollision(const btVector3& hit_normal, btScalar tangentMag = btScalar(0.0), btScalar normalMag = btScalar(1.0)); - void stepForwardAndStrafe(btCollisionWorld* collisionWorld, const btVector3& walkMove); + void stepForward(btCollisionWorld* collisionWorld, const btVector3& walkMove); void stepDown(btCollisionWorld* collisionWorld, btScalar dt); void createShapeAndGhost(); public: @@ -162,9 +163,6 @@ public: btScalar getMaxSlope() const; btPairCachingGhostObject* getGhostObject(); - void setUseGhostSweepTest(bool useGhostObjectSweepTest) { - m_useGhostObjectSweepTest = useGhostObjectSweepTest; - } bool onGround() const; void setUpInterpolate(bool value); From ab9d8eb345b2801f1d85b5e25b212281162ad6c0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 20 Mar 2015 08:42:35 -0700 Subject: [PATCH 09/26] reorder arguments to StepDownConvexResultCallback ctor --- libraries/physics/src/CharacterController.cpp | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 2c01e2ae32..fb200b2c57 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -112,21 +112,21 @@ class StepDownConvexResultCallback : public btCollisionWorld::ClosestConvexResul public: StepDownConvexResultCallback(btCollisionObject* me, const btVector3& up, - btScalar minSlopeDot, const btVector3& start, const btVector3& step, + const btVector3& pushDirection, + btScalar minSlopeDot, btScalar radius, - btScalar halfHeight, - btVector3 pushDirection) + btScalar halfHeight) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) , m_me(me) , m_up(up) - , m_minSlopeDot(minSlopeDot) , m_start(start) , m_step(step) + , m_pushDirection(pushDirection) + , m_minSlopeDot(minSlopeDot) , m_radius(radius) , m_halfHeight(halfHeight) - , m_pushDirection(pushDirection) { } @@ -184,12 +184,12 @@ class StepDownConvexResultCallback : public btCollisionWorld::ClosestConvexResul protected: btCollisionObject* m_me; const btVector3 m_up; - btScalar m_minSlopeDot; btVector3 m_start; btVector3 m_step; + btVector3 m_pushDirection; + btScalar m_minSlopeDot; btScalar m_radius; btScalar m_halfHeight; - btVector3 m_pushDirection; }; /* @@ -499,8 +499,10 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d StepDownConvexResultCallback callback(m_ghostObject, getUpAxisDirections()[m_upAxis], - m_maxSlopeCosine, m_currentPosition, - step, m_radius, m_halfHeight, m_walkDirection); + m_currentPosition, step, + m_walkDirection, + m_maxSlopeCosine, + m_radius, m_halfHeight); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; @@ -520,7 +522,12 @@ void CharacterController::stepDown( btCollisionWorld* collisionWorld, btScalar d m_wasJumping = false; } else { // sweep again for floor within downStep threshold - StepDownConvexResultCallback callback2 (m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine, m_currentPosition, step, m_radius, m_halfHeight, m_walkDirection); + StepDownConvexResultCallback callback2 (m_ghostObject, + getUpAxisDirections()[m_upAxis], + m_currentPosition, step, + m_walkDirection, + m_maxSlopeCosine, + m_radius, m_halfHeight); callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; From 7dbe92f56fb516486f9bfda20814bd495d0d0d50 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 21 Mar 2015 14:22:09 -0700 Subject: [PATCH 10/26] Building with Sixense build requires four of the SDK libraries --- interface/external/sixense/readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/external/sixense/readme.txt b/interface/external/sixense/readme.txt index 29d1bbc2f9..a4790caa5e 100644 --- a/interface/external/sixense/readme.txt +++ b/interface/external/sixense/readme.txt @@ -2,7 +2,7 @@ Instructions for adding the Sixense driver to Interface Andrzej Kapolka, November 18, 2013 -1. Copy the Sixense sdk folders (lib, include) into the interface/external/Sixense folder. This readme.txt should be there as well. +1. Copy the Sixense sdk folders (bin, include, lib, and samples) into the interface/external/Sixense folder. This readme.txt should be there as well. You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects). If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sixense' that contains the folders mentioned above. From 19c8e526e1fb630095f6ff4d0763b20ebacef784 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Mar 2015 08:47:06 -0700 Subject: [PATCH 11/26] add guards to _jointStates array size --- interface/src/avatar/FaceModel.cpp | 2 +- libraries/render-utils/src/Model.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index c80772ef49..6dd97f067a 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -87,7 +87,7 @@ void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentSta void FaceModel::updateJointState(int index) { JointState& state = _jointStates[index]; const FBXJoint& joint = state.getFBXJoint(); - if (joint.parentIndex != -1) { + if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { const JointState& parentState = _jointStates.at(joint.parentIndex); const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (index == geometry.neckJointIndex) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 0e0f081ec8..f7a4257de7 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1306,8 +1306,10 @@ void Model::updateJointState(int index) { glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; state.computeTransform(parentTransform); } else { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } } } From fe8ad9517f788bbadb9c13b95d4a9c4983ae2130 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Mar 2015 10:08:08 -0700 Subject: [PATCH 12/26] added debugging for model mesh boxes --- libraries/render-utils/src/Model.cpp | 59 +++++++++++++++++++++++++++- libraries/render-utils/src/Model.h | 3 ++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index f7a4257de7..d91c972c45 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -842,10 +842,67 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { args->_translucentMeshPartsRendered = translucentMeshPartsRendered; args->_opaqueMeshPartsRendered = opaqueMeshPartsRendered; } - + + #ifdef WANT_DEBUG_MESHBOXES + renderDebugMeshBoxes(); + #endif + return true; } +void Model::renderDebugMeshBoxes() { + int colorNdx = 0; + foreach(AABox box, _calculatedMeshBoxes) { + if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) { + _debugMeshBoxesID = DependencyManager::get()->allocateID(); + } + QVector points; + + glm::vec3 brn = box.getCorner(); + glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0); + glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z); + glm::vec3 blf = brn + glm::vec3(box.getDimensions().x, 0, box.getDimensions().z); + + glm::vec3 trn = brn + glm::vec3(0, box.getDimensions().y, 0); + glm::vec3 tln = bln + glm::vec3(0, box.getDimensions().y, 0); + glm::vec3 trf = brf + glm::vec3(0, box.getDimensions().y, 0); + glm::vec3 tlf = blf + glm::vec3(0, box.getDimensions().y, 0); + + points << brn << bln; + points << brf << blf; + points << brn << brf; + points << bln << blf; + + points << trn << tln; + points << trf << tlf; + points << trn << trf; + points << tln << tlf; + + points << brn << trn; + points << brf << trf; + points << bln << tln; + points << blf << tlf; + + glm::vec4 color[] = { + { 1.0f, 0.0f, 0.0f, 1.0f }, // red + { 0.0f, 1.0f, 0.0f, 1.0f }, // green + { 0.0f, 0.0f, 1.0f, 1.0f }, // blue + { 1.0f, 0.0f, 1.0f, 1.0f }, // purple + { 1.0f, 1.0f, 0.0f, 1.0f }, // yellow + { 0.0f, 1.0f, 1.0f, 1.0f }, // cyan + { 1.0f, 1.0f, 1.0f, 1.0f }, // white + { 0.0f, 0.5f, 0.0f, 1.0f }, + { 0.0f, 0.0f, 0.5f, 1.0f }, + { 0.5f, 0.0f, 0.5f, 1.0f }, + { 0.5f, 0.5f, 0.0f, 1.0f }, + { 0.0f, 0.5f, 0.5f, 1.0f } }; + + DependencyManager::get()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]); + DependencyManager::get()->renderVertices(gpu::LINES, _debugMeshBoxesID); + colorNdx++; + } +} + Extents Model::getBindExtents() const { if (!isActive()) { return Extents(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 5114ef1c9f..e7cb0aa8e5 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -444,6 +444,9 @@ private: QVector _meshesOpaqueLightmapTangentsSpecular; QVector _meshesOpaqueLightmapSpecular; + // debug rendering support + void renderDebugMeshBoxes(); + int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; // Scene rendering support static QVector _modelsInScene; From b705d7633685c7f4ff81af45ffed3364632cb13b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Mar 2015 10:43:21 -0700 Subject: [PATCH 13/26] total hack to ignore frustum culling in case of avatar mesh parts with no transform --- libraries/render-utils/src/Model.cpp | 49 ++++++++++++++++++---------- libraries/render-utils/src/Model.h | 7 ++-- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d91c972c45..0b7fd3dcbf 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -727,19 +727,19 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { //renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, book isSkinned, args); int opaqueMeshPartsRendered = 0; - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, true, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, true, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, args); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, false, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, true, true, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, false, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, false, true, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, false, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, true, true, true, args, true); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, args); - opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, args); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, false, false, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, false, true, false, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, false, false, args, true); + opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, true, true, true, false, args, true); // render translucent meshes afterwards //DependencyManager::get()->setPrimaryDrawBuffers(false, true, true); @@ -2358,7 +2358,8 @@ int Model::renderMeshesForModelsInScene(gpu::Batch& batch, RenderMode mode, bool } int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args) { + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args, + bool forceRenderSomeMeshes) { PROFILE_RANGE(__FUNCTION__); int meshPartsRendered = 0; @@ -2378,8 +2379,10 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl Locations* locations; SkinLocations* skinLocations; - pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, args, locations, skinLocations); - meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, args, locations, skinLocations); + pickPrograms(batch, mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, + args, locations, skinLocations); + meshPartsRendered = renderMeshesFromList(list, batch, mode, translucent, alphaThreshold, + args, locations, skinLocations, forceRenderSomeMeshes); GLBATCH(glUseProgram)(0); return meshPartsRendered; @@ -2387,7 +2390,7 @@ int Model::renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, fl int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, RenderArgs* args, - Locations* locations, SkinLocations* skinLocations) { + Locations* locations, SkinLocations* skinLocations, bool forceRenderSomeMeshes) { PROFILE_RANGE(__FUNCTION__); auto textureCache = DependencyManager::get(); @@ -2423,11 +2426,21 @@ int Model::renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMod // if we got here, then check to see if this mesh is in view if (args) { bool shouldRender = true; + bool forceRender = false; args->_meshesConsidered++; if (args->_viewFrustum) { - shouldRender = args->_viewFrustum->boxInFrustum(_calculatedMeshBoxes.at(i)) != ViewFrustum::OUTSIDE; - if (shouldRender) { + + // NOTE: This is a hack to address the fact that for avatar meshes, the _calculatedMeshBoxes can be wrong + // for some meshes. Those meshes where the mesh's modelTransform is the identity matrix, and will have + // incorrectly calculated mesh boxes. In this case, we will ignore the box and assume it's visible. + if (forceRenderSomeMeshes && (geometry.meshes.at(i).modelTransform == glm::mat4())) { + forceRender = true; + } + + shouldRender = forceRender || args->_viewFrustum->boxInFrustum(_calculatedMeshBoxes.at(i)) != ViewFrustum::OUTSIDE; + + if (shouldRender && !forceRender) { float distance = args->_viewFrustum->distanceToCamera(_calculatedMeshBoxes.at(i).calcCenter()); shouldRender = !_viewState ? false : _viewState->shouldRenderMesh(_calculatedMeshBoxes.at(i).getLargestDimension(), distance); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e7cb0aa8e5..05db20b056 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -459,12 +459,15 @@ private: void renderSetup(RenderArgs* args); bool renderCore(float alpha, RenderMode mode, RenderArgs* args); int renderMeshes(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL); + bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args = NULL, + bool forceRenderSomeMeshes = false); + void setupBatchTransform(gpu::Batch& batch); QVector* pickMeshList(bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned); int renderMeshesFromList(QVector& list, gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, - RenderArgs* args, Locations* locations, SkinLocations* skinLocations); + RenderArgs* args, Locations* locations, SkinLocations* skinLocations, + bool forceRenderSomeMeshes = false); static void pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold, bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, RenderArgs* args, From 3e8508f1493c654fde86d917facf68af35a70da3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Mar 2015 11:13:35 -0700 Subject: [PATCH 14/26] CR feedback --- libraries/render-utils/src/Model.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 0b7fd3dcbf..d640876086 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1363,6 +1363,7 @@ void Model::updateJointState(int index) { glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; state.computeTransform(parentTransform); } else { + // guard against out-of-bounds access to _jointStates if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { const JointState& parentState = _jointStates.at(parentIndex); state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); From 9fb53e3d72e8618401a80424f97e5756ecb6500f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Mar 2015 11:13:42 -0700 Subject: [PATCH 15/26] CR feedback --- interface/src/avatar/FaceModel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 6dd97f067a..722f998f86 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -87,6 +87,7 @@ void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentSta void FaceModel::updateJointState(int index) { JointState& state = _jointStates[index]; const FBXJoint& joint = state.getFBXJoint(); + // guard against out-of-bounds access to _jointStates if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) { const JointState& parentState = _jointStates.at(joint.parentIndex); const FBXGeometry& geometry = _geometry->getFBXGeometry(); From fa122a227b085f7ddaab53d02c12067229604a61 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Mar 2015 16:44:07 -0700 Subject: [PATCH 16/26] tweaks to the LOD Tools UI --- interface/src/Application.cpp | 2 +- interface/src/LODManager.cpp | 200 ++++++++++++++++++---------- interface/src/LODManager.h | 46 ++++--- interface/src/Menu.cpp | 1 - interface/src/Menu.h | 1 - interface/src/ui/LodToolsDialog.cpp | 161 ++++++++++------------ interface/src/ui/LodToolsDialog.h | 19 ++- 7 files changed, 241 insertions(+), 189 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4ff41e1b6f..ad6a0d86ce 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1969,7 +1969,7 @@ bool Application::isLookingAtMyAvatar(Avatar* avatar) { void Application::updateLOD() { PerformanceTimer perfTimer("LOD"); // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode - if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) { + if (!isThrottleRendering()) { DependencyManager::get()->autoAdjustLOD(_fps); } else { DependencyManager::get()->resetLODAdjust(); diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 063ba13492..739e5f280a 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -17,15 +17,34 @@ #include "LODManager.h" -Setting::Handle automaticAvatarLOD("automaticAvatarLOD", true); -Setting::Handle avatarLODDecreaseFPS("avatarLODDecreaseFPS", DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS); -Setting::Handle avatarLODIncreaseFPS("avatarLODIncreaseFPS", ADJUST_LOD_UP_FPS); +Setting::Handle automaticLODAdjust("automaticLODAdjust", true); +Setting::Handle desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS); +Setting::Handle desktopLODIncreaseFPS("desktopLODIncreaseFPS", DEFAULT_DESKTOP_LOD_UP_FPS); +Setting::Handle hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS); +Setting::Handle hmdLODIncreaseFPS("hmdLODIncreaseFPS", DEFAULT_HMD_LOD_UP_FPS); + + Setting::Handle avatarLODDistanceMultiplier("avatarLODDistanceMultiplier", DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER); Setting::Handle boundaryLevelAdjust("boundaryLevelAdjust", 0); Setting::Handle octreeSizeScale("octreeSizeScale", DEFAULT_OCTREE_SIZE_SCALE); +float LODManager::getLODDecreaseFPS() { + if (Application::getInstance()->isHMDMode()) { + return getHMDLODDecreaseFPS(); + } + return getDesktopLODDecreaseFPS(); +} + +float LODManager::getLODIncreaseFPS() { + if (Application::getInstance()->isHMDMode()) { + return getHMDLODIncreaseFPS(); + } + return getDesktopLODIncreaseFPS(); +} + + void LODManager::autoAdjustLOD(float currentFPS) { // NOTE: our first ~100 samples at app startup are completely all over the place, and we don't // really want to count them in our average, so we will ignore the real frame rates and stuff @@ -39,66 +58,93 @@ void LODManager::autoAdjustLOD(float currentFPS) { _fastFPSAverage.updateAverage(currentFPS); quint64 now = usecTimestampNow(); - - const quint64 ADJUST_AVATAR_LOD_DOWN_DELAY = 1000 * 1000; - if (_automaticAvatarLOD) { - if (_fastFPSAverage.getAverage() < _avatarLODDecreaseFPS) { - if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) { - // attempt to lower the detail in proportion to the fps difference - float targetFps = (_avatarLODDecreaseFPS + _avatarLODIncreaseFPS) * 0.5f; - float averageFps = _fastFPSAverage.getAverage(); - const float MAXIMUM_MULTIPLIER_SCALE = 2.0f; - _avatarLODDistanceMultiplier = qMin(MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, _avatarLODDistanceMultiplier * - (averageFps < EPSILON ? MAXIMUM_MULTIPLIER_SCALE : - qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps))); - _lastAvatarDetailDrop = now; - } - } else if (_fastFPSAverage.getAverage() > _avatarLODIncreaseFPS) { - // let the detail level creep slowly upwards - const float DISTANCE_DECREASE_RATE = 0.05f; - _avatarLODDistanceMultiplier = qMax(MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, - _avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE); - } - } - + bool changed = false; + bool octreeChanged = false; quint64 elapsed = now - _lastAdjust; - if (elapsed > ADJUST_LOD_DOWN_DELAY && _fpsAverage.getAverage() < ADJUST_LOD_DOWN_FPS - && _octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) { - - _octreeSizeScale *= ADJUST_LOD_DOWN_BY; - - if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { - _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; - } - changed = true; - _lastAdjust = now; - qDebug() << "adjusting LOD down... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() - << "_octreeSizeScale=" << _octreeSizeScale; - - emit LODDecreased(); - } - - if (elapsed > ADJUST_LOD_UP_DELAY && _fpsAverage.getAverage() > ADJUST_LOD_UP_FPS - && _octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) { - _octreeSizeScale *= ADJUST_LOD_UP_BY; - if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) { - _octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE; - } - changed = true; - _lastAdjust = now; - qDebug() << "adjusting LOD up... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() - << "_octreeSizeScale=" << _octreeSizeScale; + if (_automaticLODAdjust) { + // LOD Downward adjustment + if (elapsed > ADJUST_LOD_DOWN_DELAY && _fpsAverage.getAverage() < getLODDecreaseFPS()) { - emit LODIncreased(); - } + // Avatars... attempt to lower the detail in proportion to the fps difference + float targetFps = (getLODDecreaseFPS() + getLODIncreaseFPS()) * 0.5f; + float averageFps = _fastFPSAverage.getAverage(); + const float MAXIMUM_MULTIPLIER_SCALE = 2.0f; + float oldAvatarLODDistanceMultiplier = _avatarLODDistanceMultiplier; + _avatarLODDistanceMultiplier = qMin(MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, _avatarLODDistanceMultiplier * + (averageFps < EPSILON ? MAXIMUM_MULTIPLIER_SCALE : + qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps))); + + if (oldAvatarLODDistanceMultiplier != _avatarLODDistanceMultiplier) { + qDebug() << "adjusting LOD down... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() + << "_avatarLODDistanceMultiplier=" << _avatarLODDistanceMultiplier; + changed = true; + } + + // Octree items... stepwise adjustment + if (_octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) { + _octreeSizeScale *= ADJUST_LOD_DOWN_BY; + if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { + _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; + } + octreeChanged = changed = true; + } + + if (changed) { + _lastAdjust = now; + qDebug() << "adjusting LOD down... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() + << "_octreeSizeScale=" << _octreeSizeScale; + + emit LODDecreased(); + } + } - if (changed) { - _shouldRenderTableNeedsRebuilding = true; - auto lodToolsDialog = DependencyManager::get()->getLodToolsDialog(); - if (lodToolsDialog) { - lodToolsDialog->reloadSliders(); + // LOD Upward adjustment + if (elapsed > ADJUST_LOD_UP_DELAY && _fpsAverage.getAverage() > getLODIncreaseFPS()) { + + // Avatars... let the detail level creep slowly upwards + if (_avatarLODDistanceMultiplier < MAXIMUM_AUTO_ADJUST_AVATAR_LOD_DISTANCE_MULTIPLIER) { + const float DISTANCE_DECREASE_RATE = 0.05f; + float oldAvatarLODDistanceMultiplier = _avatarLODDistanceMultiplier; + _avatarLODDistanceMultiplier = qMax(MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, + _avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE); + + if (oldAvatarLODDistanceMultiplier != _avatarLODDistanceMultiplier) { + qDebug() << "adjusting LOD up... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() + << "_avatarLODDistanceMultiplier=" << _avatarLODDistanceMultiplier; + changed = true; + } + } + + // Octee items... stepwise adjustment + if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) { + if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { + _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; + } else { + _octreeSizeScale *= ADJUST_LOD_UP_BY; + } + if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) { + _octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE; + } + octreeChanged = changed = true; + } + + if (changed) { + _lastAdjust = now; + qDebug() << "adjusting LOD up... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() + << "_octreeSizeScale=" << _octreeSizeScale; + + emit LODIncreased(); + } + } + + if (changed) { + _shouldRenderTableNeedsRebuilding = true; + auto lodToolsDialog = DependencyManager::get()->getLodToolsDialog(); + if (lodToolsDialog) { + lodToolsDialog->reloadSliders(); + } } } } @@ -106,7 +152,7 @@ void LODManager::autoAdjustLOD(float currentFPS) { void LODManager::resetLODAdjust() { _fpsAverage.reset(); _fastFPSAverage.reset(); - _lastAvatarDetailDrop = _lastAdjust = usecTimestampNow(); + _lastAdjust = usecTimestampNow(); } QString LODManager::getLODFeedbackText() { @@ -116,29 +162,33 @@ QString LODManager::getLODFeedbackText() { switch (boundaryLevelAdjust) { case 0: { - granularityFeedback = QString("at standard granularity."); + granularityFeedback = QString("."); } break; case 1: { - granularityFeedback = QString("at half of standard granularity."); + granularityFeedback = QString(" at half of standard granularity."); } break; case 2: { - granularityFeedback = QString("at a third of standard granularity."); + granularityFeedback = QString(" at a third of standard granularity."); } break; default: { - granularityFeedback = QString("at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1); + granularityFeedback = QString(" at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1); } break; } // distance feedback float octreeSizeScale = getOctreeSizeScale(); float relativeToDefault = octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE; + int relativeToTwentyTwenty = 20 / relativeToDefault; + QString result; if (relativeToDefault > 1.01) { - result = QString("%1 further %2").arg(relativeToDefault,8,'f',2).arg(granularityFeedback); + result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',2).arg(granularityFeedback); } else if (relativeToDefault > 0.99) { - result = QString("the default distance %1").arg(granularityFeedback); + result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback); + } else if (relativeToDefault > 0.01) { + result = QString("20:%1 or %2 of default distance for average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',3).arg(granularityFeedback); } else { - result = QString("%1 of default %2").arg(relativeToDefault,8,'f',3).arg(granularityFeedback); + result = QString("%2 of default distance for average vision%3").arg(relativeToDefault,0,'f',3).arg(granularityFeedback); } return result; } @@ -194,18 +244,24 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) { void LODManager::loadSettings() { - setAutomaticAvatarLOD(automaticAvatarLOD.get()); - setAvatarLODDecreaseFPS(avatarLODDecreaseFPS.get()); - setAvatarLODIncreaseFPS(avatarLODIncreaseFPS.get()); + setAutomaticLODAdjust(automaticLODAdjust.get()); + setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get()); + setDesktopLODIncreaseFPS(desktopLODIncreaseFPS.get()); + setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get()); + setHMDLODIncreaseFPS(hmdLODIncreaseFPS.get()); + setAvatarLODDistanceMultiplier(avatarLODDistanceMultiplier.get()); setBoundaryLevelAdjust(boundaryLevelAdjust.get()); setOctreeSizeScale(octreeSizeScale.get()); } void LODManager::saveSettings() { - automaticAvatarLOD.set(getAutomaticAvatarLOD()); - avatarLODDecreaseFPS.set(getAvatarLODDecreaseFPS()); - avatarLODIncreaseFPS.set(getAvatarLODIncreaseFPS()); + automaticLODAdjust.set(getAutomaticLODAdjust()); + desktopLODDecreaseFPS.set(getDesktopLODDecreaseFPS()); + desktopLODIncreaseFPS.set(getDesktopLODIncreaseFPS()); + hmdLODDecreaseFPS.set(getHMDLODDecreaseFPS()); + hmdLODIncreaseFPS.set(getHMDLODIncreaseFPS()); + avatarLODDistanceMultiplier.set(getAvatarLODDistanceMultiplier()); boundaryLevelAdjust.set(getBoundaryLevelAdjust()); octreeSizeScale.set(getOctreeSizeScale()); diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 61c24bf5af..57b03cba42 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -17,9 +17,10 @@ #include #include -const float ADJUST_LOD_DOWN_FPS = 40.0; -const float ADJUST_LOD_UP_FPS = 55.0; -const float DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS = 30.0f; +const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0; +const float DEFAULT_DESKTOP_LOD_UP_FPS = 50.0; +const float DEFAULT_HMD_LOD_DOWN_FPS = 60.0; +const float DEFAULT_HMD_LOD_UP_FPS = 65.0; const quint64 ADJUST_LOD_DOWN_DELAY = 1000 * 1000 * 0.5; // Consider adjusting LOD down after half a second const quint64 ADJUST_LOD_UP_DELAY = ADJUST_LOD_DOWN_DELAY * 2; @@ -36,6 +37,7 @@ const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE; const float MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER = 0.1f; const float MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER = 15.0f; const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f; +const float MAXIMUM_AUTO_ADJUST_AVATAR_LOD_DISTANCE_MULTIPLIER = DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER; const int ONE_SECOND_OF_FRAMES = 60; const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES; @@ -46,14 +48,21 @@ class LODManager : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - void setAutomaticAvatarLOD(bool automaticAvatarLOD) { _automaticAvatarLOD = automaticAvatarLOD; } - bool getAutomaticAvatarLOD() const { return _automaticAvatarLOD; } - void setAvatarLODDecreaseFPS(float avatarLODDecreaseFPS) { _avatarLODDecreaseFPS = avatarLODDecreaseFPS; } - float getAvatarLODDecreaseFPS() const { return _avatarLODDecreaseFPS; } - void setAvatarLODIncreaseFPS(float avatarLODIncreaseFPS) { _avatarLODIncreaseFPS = avatarLODIncreaseFPS; } - float getAvatarLODIncreaseFPS() const { return _avatarLODIncreaseFPS; } - void setAvatarLODDistanceMultiplier(float multiplier) { _avatarLODDistanceMultiplier = multiplier; } - float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } + Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; } + Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; } + + Q_INVOKABLE void setDesktopLODDecreaseFPS(float value) { _desktopLODDecreaseFPS = value; } + Q_INVOKABLE float getDesktopLODDecreaseFPS() const { return _desktopLODDecreaseFPS; } + Q_INVOKABLE void setDesktopLODIncreaseFPS(float value) { _desktopLODIncreaseFPS = value; } + Q_INVOKABLE float getDesktopLODIncreaseFPS() const { return _desktopLODIncreaseFPS; } + + Q_INVOKABLE void setHMDLODDecreaseFPS(float value) { _hmdLODDecreaseFPS = value; } + Q_INVOKABLE float getHMDLODDecreaseFPS() const { return _hmdLODDecreaseFPS; } + Q_INVOKABLE void setHMDLODIncreaseFPS(float value) { _hmdLODIncreaseFPS = value; } + Q_INVOKABLE float getHMDLODIncreaseFPS() const { return _hmdLODIncreaseFPS; } + + Q_INVOKABLE void setAvatarLODDistanceMultiplier(float multiplier) { _avatarLODDistanceMultiplier = multiplier; } + Q_INVOKABLE float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } // User Tweakable LOD Items Q_INVOKABLE QString getLODFeedbackText(); @@ -63,12 +72,15 @@ public: Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust); Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } - void autoAdjustLOD(float currentFPS); Q_INVOKABLE void resetLODAdjust(); Q_INVOKABLE float getFPSAverage() const { return _fpsAverage.getAverage(); } Q_INVOKABLE float getFastFPSAverage() const { return _fastFPSAverage.getAverage(); } + Q_INVOKABLE float getLODDecreaseFPS(); + Q_INVOKABLE float getLODIncreaseFPS(); + bool shouldRenderMesh(float largestDimension, float distanceToCamera); + void autoAdjustLOD(float currentFPS); void loadSettings(); void saveSettings(); @@ -80,16 +92,18 @@ signals: private: LODManager() {} - bool _automaticAvatarLOD = true; - float _avatarLODDecreaseFPS = DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS; - float _avatarLODIncreaseFPS = ADJUST_LOD_UP_FPS; + bool _automaticLODAdjust = true; + float _desktopLODDecreaseFPS = DEFAULT_DESKTOP_LOD_DOWN_FPS; + float _desktopLODIncreaseFPS = DEFAULT_DESKTOP_LOD_UP_FPS; + float _hmdLODDecreaseFPS = DEFAULT_HMD_LOD_DOWN_FPS; + float _hmdLODIncreaseFPS = DEFAULT_HMD_LOD_UP_FPS; + float _avatarLODDistanceMultiplier = DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER; float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE; int _boundaryLevelAdjust = 0; quint64 _lastAdjust = 0; - quint64 _lastAvatarDetailDrop = 0; SimpleMovingAverage _fpsAverage = FIVE_SECONDS_OF_FRAMES; SimpleMovingAverage _fastFPSAverage = ONE_SECOND_OF_FRAMES; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2823e8eb23..8d3c01320f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -276,7 +276,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Entities, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DisableAutoAdjustLOD); QMenu* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight); QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index fc1347fa27..60fe5d4cd2 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -136,7 +136,6 @@ namespace MenuOption { const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger"; - const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD"; const QString DisableLightEntities = "Disable Light Entities"; const QString DisableNackPackets = "Disable NACK Packets"; const QString DiskCacheEditor = "Disk Cache Editor"; diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index 277d634735..af434d5565 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -35,9 +35,51 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : // Create layouter QFormLayout* form = new QFormLayout(this); + // Create a label with feedback... + _feedback = new QLabel(this); + QPalette palette = _feedback->palette(); + const unsigned redish = 0xfff00000; + palette.setColor(QPalette::WindowText, QColor::fromRgb(redish)); + _feedback->setPalette(palette); + _feedback->setText(lodManager->getLODFeedbackText()); + const int FEEDBACK_WIDTH = 350; + _feedback->setFixedWidth(FEEDBACK_WIDTH); + form->addRow("You can see... ", _feedback); + + form->addRow("Automatic LOD Adjustment:", _automaticLODAdjust = new QCheckBox(this)); + _automaticLODAdjust->setChecked(lodManager->getAutomaticLODAdjust()); + connect(_automaticLODAdjust, SIGNAL(toggled(bool)), SLOT(updateAutomaticLODAdjust())); + + form->addRow("Desktop Decrease LOD Below FPS:", _desktopLODDecreaseFPS = new QDoubleSpinBox(this)); + _desktopLODDecreaseFPS->setValue(lodManager->getDesktopLODDecreaseFPS()); + _desktopLODDecreaseFPS->setDecimals(0); + connect(_desktopLODDecreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateLODValues())); + + form->addRow("Desktop Increase LOD Above FPS:", _desktopLODIncreaseFPS = new QDoubleSpinBox(this)); + _desktopLODIncreaseFPS->setValue(lodManager->getDesktopLODIncreaseFPS()); + _desktopLODIncreaseFPS->setDecimals(0); + connect(_desktopLODIncreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateLODValues())); + + form->addRow("HMD Decrease LOD Below FPS:", _hmdLODDecreaseFPS = new QDoubleSpinBox(this)); + _hmdLODDecreaseFPS->setValue(lodManager->getHMDLODDecreaseFPS()); + _hmdLODDecreaseFPS->setDecimals(0); + connect(_hmdLODDecreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateLODValues())); + + form->addRow("HMD Increase LOD Above FPS:", _hmdLODIncreaseFPS = new QDoubleSpinBox(this)); + _hmdLODIncreaseFPS->setValue(lodManager->getHMDLODIncreaseFPS()); + _hmdLODIncreaseFPS->setDecimals(0); + connect(_hmdLODIncreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateLODValues())); + + form->addRow("Avatar LOD:", _avatarLOD = new QDoubleSpinBox(this)); + _avatarLOD->setDecimals(3); + _avatarLOD->setRange(1.0 / MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, 1.0 / MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER); + _avatarLOD->setSingleStep(0.001); + _avatarLOD->setValue(1.0 / lodManager->getAvatarLODDistanceMultiplier()); + connect(_avatarLOD, SIGNAL(valueChanged(double)), SLOT(updateAvatarLODValues())); + _lodSize = new QSlider(Qt::Horizontal, this); const int MAX_LOD_SIZE = MAX_LOD_SIZE_MULTIPLIER; - const int MIN_LOD_SIZE = 0; + const int MIN_LOD_SIZE = ADJUST_LOD_MIN_SIZE_SCALE; const int STEP_LOD_SIZE = 1; const int PAGE_STEP_LOD_SIZE = 100; const int SLIDER_WIDTH = 300; @@ -50,55 +92,8 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : _lodSize->setPageStep(PAGE_STEP_LOD_SIZE); int sliderValue = lodManager->getOctreeSizeScale() / TREE_SCALE; _lodSize->setValue(sliderValue); - form->addRow("LOD Size Scale:", _lodSize); + form->addRow("Non-Avatar Content LOD:", _lodSize); connect(_lodSize,SIGNAL(valueChanged(int)),this,SLOT(sizeScaleValueChanged(int))); - - _boundaryLevelAdjust = new QSlider(Qt::Horizontal, this); - const int MAX_ADJUST = 10; - const int MIN_ADJUST = 0; - const int STEP_ADJUST = 1; - _boundaryLevelAdjust->setMaximum(MAX_ADJUST); - _boundaryLevelAdjust->setMinimum(MIN_ADJUST); - _boundaryLevelAdjust->setSingleStep(STEP_ADJUST); - _boundaryLevelAdjust->setTickInterval(STEP_ADJUST); - _boundaryLevelAdjust->setTickPosition(QSlider::TicksBelow); - _boundaryLevelAdjust->setFixedWidth(SLIDER_WIDTH); - sliderValue = lodManager->getBoundaryLevelAdjust(); - _boundaryLevelAdjust->setValue(sliderValue); - form->addRow("Boundary Level Adjust:", _boundaryLevelAdjust); - connect(_boundaryLevelAdjust,SIGNAL(valueChanged(int)),this,SLOT(boundaryLevelValueChanged(int))); - - // Create a label with feedback... - _feedback = new QLabel(this); - QPalette palette = _feedback->palette(); - const unsigned redish = 0xfff00000; - palette.setColor(QPalette::WindowText, QColor::fromRgb(redish)); - _feedback->setPalette(palette); - _feedback->setText(lodManager->getLODFeedbackText()); - const int FEEDBACK_WIDTH = 350; - _feedback->setFixedWidth(FEEDBACK_WIDTH); - form->addRow("You can see... ", _feedback); - - form->addRow("Automatic Avatar LOD Adjustment:", _automaticAvatarLOD = new QCheckBox(this)); - _automaticAvatarLOD->setChecked(lodManager->getAutomaticAvatarLOD()); - connect(_automaticAvatarLOD, SIGNAL(toggled(bool)), SLOT(updateAvatarLODControls())); - - form->addRow("Decrease Avatar LOD Below FPS:", _avatarLODDecreaseFPS = new QDoubleSpinBox(this)); - _avatarLODDecreaseFPS->setValue(lodManager->getAvatarLODDecreaseFPS()); - _avatarLODDecreaseFPS->setDecimals(0); - connect(_avatarLODDecreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateAvatarLODValues())); - - form->addRow("Increase Avatar LOD Above FPS:", _avatarLODIncreaseFPS = new QDoubleSpinBox(this)); - _avatarLODIncreaseFPS->setValue(lodManager->getAvatarLODIncreaseFPS()); - _avatarLODIncreaseFPS->setDecimals(0); - connect(_avatarLODIncreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateAvatarLODValues())); - - form->addRow("Avatar LOD:", _avatarLOD = new QDoubleSpinBox(this)); - _avatarLOD->setDecimals(3); - _avatarLOD->setRange(1.0 / MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, 1.0 / MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER); - _avatarLOD->setSingleStep(0.001); - _avatarLOD->setValue(1.0 / lodManager->getAvatarLODDistanceMultiplier()); - connect(_avatarLOD, SIGNAL(valueChanged(double)), SLOT(updateAvatarLODValues())); // Add a button to reset QPushButton* resetButton = new QPushButton("Reset", this); @@ -107,49 +102,34 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : this->QDialog::setLayout(form); - updateAvatarLODControls(); + updateAutomaticLODAdjust(); } void LodToolsDialog::reloadSliders() { auto lodManager = DependencyManager::get(); _lodSize->setValue(lodManager->getOctreeSizeScale() / TREE_SCALE); - _boundaryLevelAdjust->setValue(lodManager->getBoundaryLevelAdjust()); _feedback->setText(lodManager->getLODFeedbackText()); + + _avatarLOD->setValue(1.0 / lodManager->getAvatarLODDistanceMultiplier()); + } -void LodToolsDialog::updateAvatarLODControls() { - QFormLayout* form = static_cast(layout()); - +void LodToolsDialog::updateAutomaticLODAdjust() { auto lodManager = DependencyManager::get(); - lodManager->setAutomaticAvatarLOD(_automaticAvatarLOD->isChecked()); - - _avatarLODDecreaseFPS->setVisible(_automaticAvatarLOD->isChecked()); - form->labelForField(_avatarLODDecreaseFPS)->setVisible(_automaticAvatarLOD->isChecked()); - - _avatarLODIncreaseFPS->setVisible(_automaticAvatarLOD->isChecked()); - form->labelForField(_avatarLODIncreaseFPS)->setVisible(_automaticAvatarLOD->isChecked()); - - _avatarLOD->setVisible(!_automaticAvatarLOD->isChecked()); - form->labelForField(_avatarLOD)->setVisible(!_automaticAvatarLOD->isChecked()); - - if (!_automaticAvatarLOD->isChecked()) { - _avatarLOD->setValue(1.0 / lodManager->getAvatarLODDistanceMultiplier()); - } - - if (isVisible()) { - adjustSize(); - } + lodManager->setAutomaticLODAdjust(_automaticLODAdjust->isChecked()); } -void LodToolsDialog::updateAvatarLODValues() { +void LodToolsDialog::updateLODValues() { auto lodManager = DependencyManager::get(); - if (_automaticAvatarLOD->isChecked()) { - lodManager->setAvatarLODDecreaseFPS(_avatarLODDecreaseFPS->value()); - lodManager->setAvatarLODIncreaseFPS(_avatarLODIncreaseFPS->value()); - - } else { - lodManager->setAvatarLODDistanceMultiplier(1.0 / _avatarLOD->value()); - } + + lodManager->setAutomaticLODAdjust(_automaticLODAdjust->isChecked()); + + lodManager->setDesktopLODDecreaseFPS(_desktopLODDecreaseFPS->value()); + lodManager->setDesktopLODIncreaseFPS(_desktopLODIncreaseFPS->value()); + lodManager->setHMDLODDecreaseFPS(_hmdLODDecreaseFPS->value()); + lodManager->setHMDLODIncreaseFPS(_hmdLODIncreaseFPS->value()); + + lodManager->setAvatarLODDistanceMultiplier(1.0 / _avatarLOD->value()); } void LodToolsDialog::sizeScaleValueChanged(int value) { @@ -160,20 +140,19 @@ void LodToolsDialog::sizeScaleValueChanged(int value) { _feedback->setText(lodManager->getLODFeedbackText()); } -void LodToolsDialog::boundaryLevelValueChanged(int value) { - auto lodManager = DependencyManager::get(); - lodManager->setBoundaryLevelAdjust(value); - _feedback->setText(lodManager->getLODFeedbackText()); -} - void LodToolsDialog::resetClicked(bool checked) { + int sliderValue = DEFAULT_OCTREE_SIZE_SCALE / TREE_SCALE; - //sizeScaleValueChanged(sliderValue); _lodSize->setValue(sliderValue); - _boundaryLevelAdjust->setValue(0); - _automaticAvatarLOD->setChecked(true); - _avatarLODDecreaseFPS->setValue(DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS); - _avatarLODIncreaseFPS->setValue(ADJUST_LOD_UP_FPS); + _automaticLODAdjust->setChecked(true); + _desktopLODDecreaseFPS->setValue(DEFAULT_DESKTOP_LOD_DOWN_FPS); + _desktopLODIncreaseFPS->setValue(DEFAULT_DESKTOP_LOD_UP_FPS); + _hmdLODDecreaseFPS->setValue(DEFAULT_HMD_LOD_DOWN_FPS); + _hmdLODIncreaseFPS->setValue(DEFAULT_HMD_LOD_UP_FPS); + + _avatarLOD->setValue(1.0 / DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER); + + updateLODValues(); // tell our LOD manager about the reset } void LodToolsDialog::reject() { diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index 772027790c..2e85b3f5a5 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -31,11 +31,10 @@ signals: public slots: void reject(); void sizeScaleValueChanged(int value); - void boundaryLevelValueChanged(int value); void resetClicked(bool checked); void reloadSliders(); - void updateAvatarLODControls(); - void updateAvatarLODValues(); + void updateAutomaticLODAdjust(); + void updateLODValues(); protected: @@ -44,10 +43,16 @@ protected: private: QSlider* _lodSize; - QSlider* _boundaryLevelAdjust; - QCheckBox* _automaticAvatarLOD; - QDoubleSpinBox* _avatarLODDecreaseFPS; - QDoubleSpinBox* _avatarLODIncreaseFPS; + + QCheckBox* _automaticLODAdjust; + + QDoubleSpinBox* _desktopLODDecreaseFPS; + QDoubleSpinBox* _desktopLODIncreaseFPS; + + QDoubleSpinBox* _hmdLODDecreaseFPS; + QDoubleSpinBox* _hmdLODIncreaseFPS; + + QDoubleSpinBox* _avatarLOD; QLabel* _feedback; }; From 69f261ae943de395bd3ef27eec684f334554b63d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Mar 2015 16:44:19 -0700 Subject: [PATCH 17/26] tweaks to the LOD Tools UI --- libraries/octree/src/OctreeConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/octree/src/OctreeConstants.h b/libraries/octree/src/OctreeConstants.h index a975469053..1246aeffd4 100644 --- a/libraries/octree/src/OctreeConstants.h +++ b/libraries/octree/src/OctreeConstants.h @@ -23,7 +23,7 @@ const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of t const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * 400.0f; // This is used in the LOD Tools to translate between the size scale slider and the values used to set the OctreeSizeScale -const float MAX_LOD_SIZE_MULTIPLIER = 2000.0f; +const float MAX_LOD_SIZE_MULTIPLIER = 800.0f; const int NUMBER_OF_CHILDREN = 8; From 168e8e8b808f6c03af5c1f0653fe71d252044266 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Mar 2015 08:31:45 -0700 Subject: [PATCH 18/26] more fixes to avatar parts not rendering --- libraries/render-utils/src/Model.cpp | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d640876086..873f458ccf 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -725,7 +725,7 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { const float DEFAULT_ALPHA_THRESHOLD = 0.5f; - //renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, book isSkinned, args); + //renderMeshes(batch, mode, translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned, args, forceRenderMeshes); int opaqueMeshPartsRendered = 0; opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, args, true); opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, args, true); @@ -753,14 +753,14 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { int translucentMeshPartsRendered = 0; const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, args); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, args, true); GLBATCH(glDisable)(GL_ALPHA_TEST); GLBATCH(glEnable)(GL_BLEND); @@ -777,14 +777,14 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) { const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f; - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, args); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, args, true); } GLBATCH(glDepthMask)(true); From a9e4d25f97ea27178b0622d0cc2774f8c846272d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Mar 2015 08:31:45 -0700 Subject: [PATCH 19/26] more fixes to avatar parts not rendering --- libraries/render-utils/src/Model.cpp | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d640876086..873f458ccf 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -725,7 +725,7 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { const float DEFAULT_ALPHA_THRESHOLD = 0.5f; - //renderMeshes(RenderMode mode, bool translucent, float alphaThreshold, bool hasTangents, bool hasSpecular, book isSkinned, args); + //renderMeshes(batch, mode, translucent, alphaThreshold, hasTangents, hasSpecular, isSkinned, args, forceRenderMeshes); int opaqueMeshPartsRendered = 0; opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, false, args, true); opaqueMeshPartsRendered += renderMeshes(batch, mode, false, DEFAULT_ALPHA_THRESHOLD, false, false, false, true, args, true); @@ -753,14 +753,14 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { int translucentMeshPartsRendered = 0; const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, args); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, false, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, false, true, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, false, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_OPAQUE_THRESHOLD, false, true, true, true, args, true); GLBATCH(glDisable)(GL_ALPHA_TEST); GLBATCH(glEnable)(GL_BLEND); @@ -777,14 +777,14 @@ bool Model::renderCore(float alpha, RenderMode mode, RenderArgs* args) { if (mode == DEFAULT_RENDER_MODE || mode == DIFFUSE_RENDER_MODE) { const float MOSTLY_TRANSPARENT_THRESHOLD = 0.0f; - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, args); - translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, args); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, false, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, false, true, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, false, true, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, false, args, true); + translucentMeshPartsRendered += renderMeshes(batch, mode, true, MOSTLY_TRANSPARENT_THRESHOLD, false, true, true, true, args, true); } GLBATCH(glDepthMask)(true); From 1a6865b41bfe2600bc8b5e7be777d23fa6d40612 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 24 Mar 2015 12:26:32 -0700 Subject: [PATCH 20/26] send immediate location update when domain changes --- interface/src/Application.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ad6a0d86ce..c21d221303 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -382,6 +382,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : auto discoverabilityManager = DependencyManager::get(); connect(locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); + + // if we get a domain change, immediately attempt update location in metaverse server + connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, + discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded); connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled); @@ -3295,11 +3299,6 @@ void Application::connectedToDomain(const QString& hostname) { const QUuid& domainID = DependencyManager::get()->getDomainHandler().getUUID(); if (accountManager.isLoggedIn() && !domainID.isNull()) { - // update our data-server with the domain-server we're logged in with - QString domainPutJsonString = "{\"location\":{\"domain_id\":\"" + uuidStringWithoutCurlyBraces(domainID) + "\"}}"; - accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation, - JSONCallbackParameters(), domainPutJsonString.toUtf8()); - _notifiedPacketVersionMismatchThisDomain = false; } } From 619428c325864f361c9b9ba17c31733c171ebc85 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Mar 2015 13:32:22 -0700 Subject: [PATCH 21/26] move LOD FPS settings to Preferences --- interface/src/Application.cpp | 2 + interface/src/LODManager.cpp | 9 - interface/src/LODManager.h | 11 +- interface/src/ui/LodToolsDialog.cpp | 31 ---- interface/src/ui/LodToolsDialog.h | 2 - interface/src/ui/PreferencesDialog.cpp | 10 ++ interface/ui/preferencesDialog.ui | 218 +++++++++++++++++++++++++ 7 files changed, 233 insertions(+), 50 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ad6a0d86ce..daf2a76f5f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1786,6 +1786,7 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa void Application::loadSettings() { DependencyManager::get()->loadSettings(); + DependencyManager::get()->loadSettings(); Menu::getInstance()->loadSettings(); _myAvatar->loadData(); @@ -1793,6 +1794,7 @@ void Application::loadSettings() { void Application::saveSettings() { DependencyManager::get()->saveSettings(); + DependencyManager::get()->saveSettings(); Menu::getInstance()->saveSettings(); _myAvatar->saveData(); diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 739e5f280a..5c55952e3a 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -17,11 +17,8 @@ #include "LODManager.h" -Setting::Handle automaticLODAdjust("automaticLODAdjust", true); Setting::Handle desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS); -Setting::Handle desktopLODIncreaseFPS("desktopLODIncreaseFPS", DEFAULT_DESKTOP_LOD_UP_FPS); Setting::Handle hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS); -Setting::Handle hmdLODIncreaseFPS("hmdLODIncreaseFPS", DEFAULT_HMD_LOD_UP_FPS); Setting::Handle avatarLODDistanceMultiplier("avatarLODDistanceMultiplier", @@ -244,11 +241,8 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) { void LODManager::loadSettings() { - setAutomaticLODAdjust(automaticLODAdjust.get()); setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get()); - setDesktopLODIncreaseFPS(desktopLODIncreaseFPS.get()); setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get()); - setHMDLODIncreaseFPS(hmdLODIncreaseFPS.get()); setAvatarLODDistanceMultiplier(avatarLODDistanceMultiplier.get()); setBoundaryLevelAdjust(boundaryLevelAdjust.get()); @@ -256,11 +250,8 @@ void LODManager::loadSettings() { } void LODManager::saveSettings() { - automaticLODAdjust.set(getAutomaticLODAdjust()); desktopLODDecreaseFPS.set(getDesktopLODDecreaseFPS()); - desktopLODIncreaseFPS.set(getDesktopLODIncreaseFPS()); hmdLODDecreaseFPS.set(getHMDLODDecreaseFPS()); - hmdLODIncreaseFPS.set(getHMDLODIncreaseFPS()); avatarLODDistanceMultiplier.set(getAvatarLODDistanceMultiplier()); boundaryLevelAdjust.set(getBoundaryLevelAdjust()); diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 57b03cba42..7d63fbd172 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -18,9 +18,8 @@ #include const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0; -const float DEFAULT_DESKTOP_LOD_UP_FPS = 50.0; const float DEFAULT_HMD_LOD_DOWN_FPS = 60.0; -const float DEFAULT_HMD_LOD_UP_FPS = 65.0; +const float INCREASE_LOD_GAP = 5.0f; const quint64 ADJUST_LOD_DOWN_DELAY = 1000 * 1000 * 0.5; // Consider adjusting LOD down after half a second const quint64 ADJUST_LOD_UP_DELAY = ADJUST_LOD_DOWN_DELAY * 2; @@ -53,13 +52,11 @@ public: Q_INVOKABLE void setDesktopLODDecreaseFPS(float value) { _desktopLODDecreaseFPS = value; } Q_INVOKABLE float getDesktopLODDecreaseFPS() const { return _desktopLODDecreaseFPS; } - Q_INVOKABLE void setDesktopLODIncreaseFPS(float value) { _desktopLODIncreaseFPS = value; } - Q_INVOKABLE float getDesktopLODIncreaseFPS() const { return _desktopLODIncreaseFPS; } + Q_INVOKABLE float getDesktopLODIncreaseFPS() const { return _desktopLODDecreaseFPS + INCREASE_LOD_GAP; } Q_INVOKABLE void setHMDLODDecreaseFPS(float value) { _hmdLODDecreaseFPS = value; } Q_INVOKABLE float getHMDLODDecreaseFPS() const { return _hmdLODDecreaseFPS; } - Q_INVOKABLE void setHMDLODIncreaseFPS(float value) { _hmdLODIncreaseFPS = value; } - Q_INVOKABLE float getHMDLODIncreaseFPS() const { return _hmdLODIncreaseFPS; } + Q_INVOKABLE float getHMDLODIncreaseFPS() const { return _hmdLODDecreaseFPS + INCREASE_LOD_GAP; } Q_INVOKABLE void setAvatarLODDistanceMultiplier(float multiplier) { _avatarLODDistanceMultiplier = multiplier; } Q_INVOKABLE float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } @@ -94,9 +91,7 @@ private: bool _automaticLODAdjust = true; float _desktopLODDecreaseFPS = DEFAULT_DESKTOP_LOD_DOWN_FPS; - float _desktopLODIncreaseFPS = DEFAULT_DESKTOP_LOD_UP_FPS; float _hmdLODDecreaseFPS = DEFAULT_HMD_LOD_DOWN_FPS; - float _hmdLODIncreaseFPS = DEFAULT_HMD_LOD_UP_FPS; float _avatarLODDistanceMultiplier = DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER; diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index af434d5565..7635012bc2 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -50,26 +50,6 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : _automaticLODAdjust->setChecked(lodManager->getAutomaticLODAdjust()); connect(_automaticLODAdjust, SIGNAL(toggled(bool)), SLOT(updateAutomaticLODAdjust())); - form->addRow("Desktop Decrease LOD Below FPS:", _desktopLODDecreaseFPS = new QDoubleSpinBox(this)); - _desktopLODDecreaseFPS->setValue(lodManager->getDesktopLODDecreaseFPS()); - _desktopLODDecreaseFPS->setDecimals(0); - connect(_desktopLODDecreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateLODValues())); - - form->addRow("Desktop Increase LOD Above FPS:", _desktopLODIncreaseFPS = new QDoubleSpinBox(this)); - _desktopLODIncreaseFPS->setValue(lodManager->getDesktopLODIncreaseFPS()); - _desktopLODIncreaseFPS->setDecimals(0); - connect(_desktopLODIncreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateLODValues())); - - form->addRow("HMD Decrease LOD Below FPS:", _hmdLODDecreaseFPS = new QDoubleSpinBox(this)); - _hmdLODDecreaseFPS->setValue(lodManager->getHMDLODDecreaseFPS()); - _hmdLODDecreaseFPS->setDecimals(0); - connect(_hmdLODDecreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateLODValues())); - - form->addRow("HMD Increase LOD Above FPS:", _hmdLODIncreaseFPS = new QDoubleSpinBox(this)); - _hmdLODIncreaseFPS->setValue(lodManager->getHMDLODIncreaseFPS()); - _hmdLODIncreaseFPS->setDecimals(0); - connect(_hmdLODIncreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateLODValues())); - form->addRow("Avatar LOD:", _avatarLOD = new QDoubleSpinBox(this)); _avatarLOD->setDecimals(3); _avatarLOD->setRange(1.0 / MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, 1.0 / MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER); @@ -123,12 +103,6 @@ void LodToolsDialog::updateLODValues() { auto lodManager = DependencyManager::get(); lodManager->setAutomaticLODAdjust(_automaticLODAdjust->isChecked()); - - lodManager->setDesktopLODDecreaseFPS(_desktopLODDecreaseFPS->value()); - lodManager->setDesktopLODIncreaseFPS(_desktopLODIncreaseFPS->value()); - lodManager->setHMDLODDecreaseFPS(_hmdLODDecreaseFPS->value()); - lodManager->setHMDLODIncreaseFPS(_hmdLODIncreaseFPS->value()); - lodManager->setAvatarLODDistanceMultiplier(1.0 / _avatarLOD->value()); } @@ -145,11 +119,6 @@ void LodToolsDialog::resetClicked(bool checked) { int sliderValue = DEFAULT_OCTREE_SIZE_SCALE / TREE_SCALE; _lodSize->setValue(sliderValue); _automaticLODAdjust->setChecked(true); - _desktopLODDecreaseFPS->setValue(DEFAULT_DESKTOP_LOD_DOWN_FPS); - _desktopLODIncreaseFPS->setValue(DEFAULT_DESKTOP_LOD_UP_FPS); - _hmdLODDecreaseFPS->setValue(DEFAULT_HMD_LOD_DOWN_FPS); - _hmdLODIncreaseFPS->setValue(DEFAULT_HMD_LOD_UP_FPS); - _avatarLOD->setValue(1.0 / DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER); updateLODValues(); // tell our LOD manager about the reset diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index 2e85b3f5a5..709357fddd 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -47,10 +47,8 @@ private: QCheckBox* _automaticLODAdjust; QDoubleSpinBox* _desktopLODDecreaseFPS; - QDoubleSpinBox* _desktopLODIncreaseFPS; QDoubleSpinBox* _hmdLODDecreaseFPS; - QDoubleSpinBox* _hmdLODIncreaseFPS; QDoubleSpinBox* _avatarLOD; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index fa63079290..a07de371a2 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -19,6 +19,7 @@ #include "Application.h" #include "MainWindow.h" +#include "LODManager.h" #include "Menu.h" #include "ModelsBrowser.h" #include "PreferencesDialog.h" @@ -174,6 +175,10 @@ void PreferencesDialog::loadPreferences() { ui.sixenseReticleMoveSpeedSpin->setValue(sixense.getReticleMoveSpeed()); ui.invertSixenseButtonsCheckBox->setChecked(sixense.getInvertButtons()); + // LOD items + auto lodManager = DependencyManager::get(); + ui.desktopMinimumFPSSpin->setValue(lodManager->getDesktopLODDecreaseFPS()); + ui.hmdMinimumFPSSpin->setValue(lodManager->getHMDLODDecreaseFPS()); } void PreferencesDialog::savePreferences() { @@ -275,4 +280,9 @@ void PreferencesDialog::savePreferences() { audio->setOutputStarveDetectionPeriod(ui.outputStarveDetectionPeriodSpinner->value()); Application::getInstance()->resizeGL(glCanvas->width(), glCanvas->height()); + + // LOD items + auto lodManager = DependencyManager::get(); + lodManager->setDesktopLODDecreaseFPS(ui.desktopMinimumFPSSpin->value()); + lodManager->setHMDLODDecreaseFPS(ui.hmdMinimumFPSSpin->value()); } diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index 13894a2592..d295d094c2 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -701,6 +701,219 @@ + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + Arial + 18 + 75 + true + + + + color:#29967e + + + Level of Detail Tuning + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + + 0 + + + 7 + + + 0 + + + 7 + + + + + + Arial + + + + + + + Minimum Desktop FPS + + + 0 + + + + + + + + + Arial + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 100 + 0 + + + + + 95 + 36 + + + + + Arial + + + + 0 + + + 120 + + + + + + + + + + 0 + + + 7 + + + 0 + + + 7 + + + + + + Arial + + + + + + + Minimum HMD FPS + + + 0 + + + + + + + + + Arial + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + 100 + 0 + + + + + 95 + 36 + + + + + Arial + + + + 0 + + + 120 + + + + + + + @@ -717,6 +930,7 @@ + @@ -738,6 +952,7 @@ + @@ -820,6 +1035,9 @@ + + + From 6c941d73d5a503671bc8f600273f7043ec4bb5ff Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 24 Mar 2015 13:34:14 -0700 Subject: [PATCH 22/26] Update edit.js help --- .../html/edit-entities-commands.html | 3832 +++++++++-------- 1 file changed, 1927 insertions(+), 1905 deletions(-) diff --git a/interface/resources/html/edit-entities-commands.html b/interface/resources/html/edit-entities-commands.html index afa662f089..65b985fb6a 100644 --- a/interface/resources/html/edit-entities-commands.html +++ b/interface/resources/html/edit-entities-commands.html @@ -2,2035 +2,2057 @@
- + - Edit Entity Help + - - - - - + - - - - - - - - - + + + + + + - - - - - - - + + - - - + + + - + - - - + + + - - + + - - - + + + - - - - + S213.7,23.8,213.7,25.1z"/> + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + - + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - + + + - + c0.2-0.2,0.5-0.2,0.8,0l1.8,1.2l3.1-4.4l-4.4-3.1L58,205c-0.1,0.2-0.3,0.3-0.4,0.3c-0.1,0-0.2,0-0.3,0c-0.3-0.1-0.5-0.3-0.5-0.5 + l-0.9-4.7c-0.1-0.4,0.2-0.7,0.6-0.8l4.7-0.9c0.3-0.1,0.5,0.1,0.7,0.3c0.2,0.2,0.2,0.5,0,0.8l-1.2,1.8l4.4,3.1l3.1-4.4l-1.8-1.2 + c-0.2-0.2-0.3-0.4-0.3-0.7c0.1-0.3,0.3-0.5,0.5-0.5l4.7-0.9c0.4-0.1,0.7,0.2,0.8,0.6l0.9,4.7c0.1,0.3-0.1,0.5-0.3,0.7 + c-0.1,0-0.2,0.1-0.3,0.1c-0.2,0-0.4,0-0.5-0.1l-1.8-1.2L67.1,205.6z"/> - + c-0.1,0-0.2,0.1-0.3,0.1c-0.2,0-0.4,0-0.5-0.1l-1.8-1.2L67.1,269.3z"/> - + c-0.1,0-0.2,0.1-0.3,0.1c-0.2,0-0.4,0-0.5-0.1l-1.8-1.2L67.1,332.3z"/> - - - - + + + + - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + c-0.1,0-0.2,0.1-0.3,0.1c-0.2,0-0.4,0-0.5-0.1l-1.8-1.2L687.1,208.4z"/> - - - - - + + + + + + + + - - - - - - + + + + + - - - - - - - - - - - - - - - + - + + + + + + + + + + - + + + + + + + + - - - - + + + + + + + + + + + + + c-0.1,0-0.2,0.1-0.3,0.1c-0.2,0-0.4,0-0.5-0.1l-1.8-1.2L631.5,270.6z"/> - + - + - + - + - - - - + + + + - + - + - + - + - - - + + +
From 3f986229d150875275a868ab940a37766a11988b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 24 Mar 2015 13:43:10 -0700 Subject: [PATCH 23/26] Rename edit-entities-commands.html to edit-commands.html --- .../html/{edit-entities-commands.html => edit-commands.html} | 0 interface/src/Application.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename interface/resources/html/{edit-entities-commands.html => edit-commands.html} (100%) diff --git a/interface/resources/html/edit-entities-commands.html b/interface/resources/html/edit-commands.html similarity index 100% rename from interface/resources/html/edit-entities-commands.html rename to interface/resources/html/edit-commands.html diff --git a/interface/src/Application.h b/interface/src/Application.h index 4038b21739..898db76f07 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -113,7 +113,7 @@ static const float MIRROR_FIELD_OF_VIEW = 30.0f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString INFO_HELP_PATH = "html/interface-welcome.html"; -static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-entities-commands.html"; +static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; #ifdef Q_OS_WIN static const UINT UWM_IDENTIFY_INSTANCES = From f43b8bbe8edc744d99f3d43fc665d014ee6f001d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Mar 2015 13:59:46 -0700 Subject: [PATCH 24/26] make AvatarLODMultiplier based on octreeSizeScale --- interface/src/LODManager.cpp | 46 +++++++---------------------- interface/src/LODManager.h | 14 ++++----- interface/src/ui/LodToolsDialog.cpp | 13 -------- interface/src/ui/LodToolsDialog.h | 2 -- 4 files changed, 16 insertions(+), 59 deletions(-) diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 5c55952e3a..6d08b8e242 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -20,13 +20,14 @@ Setting::Handle desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS); Setting::Handle hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS); - -Setting::Handle avatarLODDistanceMultiplier("avatarLODDistanceMultiplier", - DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER); Setting::Handle boundaryLevelAdjust("boundaryLevelAdjust", 0); Setting::Handle octreeSizeScale("octreeSizeScale", DEFAULT_OCTREE_SIZE_SCALE); +LODManager::LODManager() { + calculateAvatarLODDistanceMultiplier(); +} + float LODManager::getLODDecreaseFPS() { if (Application::getInstance()->isHMDMode()) { return getHMDLODDecreaseFPS(); @@ -64,21 +65,6 @@ void LODManager::autoAdjustLOD(float currentFPS) { // LOD Downward adjustment if (elapsed > ADJUST_LOD_DOWN_DELAY && _fpsAverage.getAverage() < getLODDecreaseFPS()) { - // Avatars... attempt to lower the detail in proportion to the fps difference - float targetFps = (getLODDecreaseFPS() + getLODIncreaseFPS()) * 0.5f; - float averageFps = _fastFPSAverage.getAverage(); - const float MAXIMUM_MULTIPLIER_SCALE = 2.0f; - float oldAvatarLODDistanceMultiplier = _avatarLODDistanceMultiplier; - _avatarLODDistanceMultiplier = qMin(MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, _avatarLODDistanceMultiplier * - (averageFps < EPSILON ? MAXIMUM_MULTIPLIER_SCALE : - qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps))); - - if (oldAvatarLODDistanceMultiplier != _avatarLODDistanceMultiplier) { - qDebug() << "adjusting LOD down... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() - << "_avatarLODDistanceMultiplier=" << _avatarLODDistanceMultiplier; - changed = true; - } - // Octree items... stepwise adjustment if (_octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) { _octreeSizeScale *= ADJUST_LOD_DOWN_BY; @@ -100,20 +86,6 @@ void LODManager::autoAdjustLOD(float currentFPS) { // LOD Upward adjustment if (elapsed > ADJUST_LOD_UP_DELAY && _fpsAverage.getAverage() > getLODIncreaseFPS()) { - // Avatars... let the detail level creep slowly upwards - if (_avatarLODDistanceMultiplier < MAXIMUM_AUTO_ADJUST_AVATAR_LOD_DISTANCE_MULTIPLIER) { - const float DISTANCE_DECREASE_RATE = 0.05f; - float oldAvatarLODDistanceMultiplier = _avatarLODDistanceMultiplier; - _avatarLODDistanceMultiplier = qMax(MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, - _avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE); - - if (oldAvatarLODDistanceMultiplier != _avatarLODDistanceMultiplier) { - qDebug() << "adjusting LOD up... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() - << "_avatarLODDistanceMultiplier=" << _avatarLODDistanceMultiplier; - changed = true; - } - } - // Octee items... stepwise adjustment if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) { if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { @@ -137,6 +109,7 @@ void LODManager::autoAdjustLOD(float currentFPS) { } if (changed) { + calculateAvatarLODDistanceMultiplier(); _shouldRenderTableNeedsRebuilding = true; auto lodToolsDialog = DependencyManager::get()->getLodToolsDialog(); if (lodToolsDialog) { @@ -231,9 +204,14 @@ bool LODManager::shouldRenderMesh(float largestDimension, float distanceToCamera void LODManager::setOctreeSizeScale(float sizeScale) { _octreeSizeScale = sizeScale; + calculateAvatarLODDistanceMultiplier(); _shouldRenderTableNeedsRebuilding = true; } +void LODManager::calculateAvatarLODDistanceMultiplier() { + _avatarLODDistanceMultiplier = AVATAR_TO_ENTITY_RATIO / (_octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE); +} + void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; _shouldRenderTableNeedsRebuilding = true; @@ -243,8 +221,6 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) { void LODManager::loadSettings() { setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get()); setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get()); - - setAvatarLODDistanceMultiplier(avatarLODDistanceMultiplier.get()); setBoundaryLevelAdjust(boundaryLevelAdjust.get()); setOctreeSizeScale(octreeSizeScale.get()); } @@ -252,8 +228,6 @@ void LODManager::loadSettings() { void LODManager::saveSettings() { desktopLODDecreaseFPS.set(getDesktopLODDecreaseFPS()); hmdLODDecreaseFPS.set(getHMDLODDecreaseFPS()); - - avatarLODDistanceMultiplier.set(getAvatarLODDistanceMultiplier()); boundaryLevelAdjust.set(getBoundaryLevelAdjust()); octreeSizeScale.set(getOctreeSizeScale()); } diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 7d63fbd172..c14f17ca61 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -33,10 +33,9 @@ const float ADJUST_LOD_UP_BY = 1.1f; const float ADJUST_LOD_MIN_SIZE_SCALE = 1.0f; const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE; -const float MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER = 0.1f; -const float MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER = 15.0f; -const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f; -const float MAXIMUM_AUTO_ADJUST_AVATAR_LOD_DISTANCE_MULTIPLIER = DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER; +// The ratio of "visibility" of avatars to other content. A value larger than 1 will mean Avatars "cull" later than entities +// do. But both are still culled using the same angular size logic. +const float AVATAR_TO_ENTITY_RATIO = 2.0f; const int ONE_SECOND_OF_FRAMES = 60; const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES; @@ -58,7 +57,6 @@ public: Q_INVOKABLE float getHMDLODDecreaseFPS() const { return _hmdLODDecreaseFPS; } Q_INVOKABLE float getHMDLODIncreaseFPS() const { return _hmdLODDecreaseFPS + INCREASE_LOD_GAP; } - Q_INVOKABLE void setAvatarLODDistanceMultiplier(float multiplier) { _avatarLODDistanceMultiplier = multiplier; } Q_INVOKABLE float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } // User Tweakable LOD Items @@ -87,14 +85,14 @@ signals: void LODDecreased(); private: - LODManager() {} + LODManager(); + void calculateAvatarLODDistanceMultiplier(); bool _automaticLODAdjust = true; float _desktopLODDecreaseFPS = DEFAULT_DESKTOP_LOD_DOWN_FPS; float _hmdLODDecreaseFPS = DEFAULT_HMD_LOD_DOWN_FPS; - float _avatarLODDistanceMultiplier = DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER; - + float _avatarLODDistanceMultiplier; float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE; int _boundaryLevelAdjust = 0; diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index 7635012bc2..97991c62a4 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -50,13 +50,6 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : _automaticLODAdjust->setChecked(lodManager->getAutomaticLODAdjust()); connect(_automaticLODAdjust, SIGNAL(toggled(bool)), SLOT(updateAutomaticLODAdjust())); - form->addRow("Avatar LOD:", _avatarLOD = new QDoubleSpinBox(this)); - _avatarLOD->setDecimals(3); - _avatarLOD->setRange(1.0 / MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, 1.0 / MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER); - _avatarLOD->setSingleStep(0.001); - _avatarLOD->setValue(1.0 / lodManager->getAvatarLODDistanceMultiplier()); - connect(_avatarLOD, SIGNAL(valueChanged(double)), SLOT(updateAvatarLODValues())); - _lodSize = new QSlider(Qt::Horizontal, this); const int MAX_LOD_SIZE = MAX_LOD_SIZE_MULTIPLIER; const int MIN_LOD_SIZE = ADJUST_LOD_MIN_SIZE_SCALE; @@ -89,9 +82,6 @@ void LodToolsDialog::reloadSliders() { auto lodManager = DependencyManager::get(); _lodSize->setValue(lodManager->getOctreeSizeScale() / TREE_SCALE); _feedback->setText(lodManager->getLODFeedbackText()); - - _avatarLOD->setValue(1.0 / lodManager->getAvatarLODDistanceMultiplier()); - } void LodToolsDialog::updateAutomaticLODAdjust() { @@ -101,9 +91,7 @@ void LodToolsDialog::updateAutomaticLODAdjust() { void LodToolsDialog::updateLODValues() { auto lodManager = DependencyManager::get(); - lodManager->setAutomaticLODAdjust(_automaticLODAdjust->isChecked()); - lodManager->setAvatarLODDistanceMultiplier(1.0 / _avatarLOD->value()); } void LodToolsDialog::sizeScaleValueChanged(int value) { @@ -119,7 +107,6 @@ void LodToolsDialog::resetClicked(bool checked) { int sliderValue = DEFAULT_OCTREE_SIZE_SCALE / TREE_SCALE; _lodSize->setValue(sliderValue); _automaticLODAdjust->setChecked(true); - _avatarLOD->setValue(1.0 / DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER); updateLODValues(); // tell our LOD manager about the reset } diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index 709357fddd..91be04e0ed 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -50,8 +50,6 @@ private: QDoubleSpinBox* _hmdLODDecreaseFPS; - - QDoubleSpinBox* _avatarLOD; QLabel* _feedback; }; From b67cf3af2435cd772302c1f8ce5bda7ef471c7f5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Mar 2015 14:33:06 -0700 Subject: [PATCH 25/26] more tweaks to LOD tools dialog --- interface/src/LODManager.cpp | 8 -------- interface/src/ui/LodToolsDialog.cpp | 29 +++++++++++++++++------------ interface/src/ui/LodToolsDialog.h | 3 +-- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 6d08b8e242..8b942dcfe1 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -20,10 +20,6 @@ Setting::Handle desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS); Setting::Handle hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS); -Setting::Handle boundaryLevelAdjust("boundaryLevelAdjust", 0); -Setting::Handle octreeSizeScale("octreeSizeScale", DEFAULT_OCTREE_SIZE_SCALE); - - LODManager::LODManager() { calculateAvatarLODDistanceMultiplier(); } @@ -221,15 +217,11 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) { void LODManager::loadSettings() { setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get()); setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get()); - setBoundaryLevelAdjust(boundaryLevelAdjust.get()); - setOctreeSizeScale(octreeSizeScale.get()); } void LODManager::saveSettings() { desktopLODDecreaseFPS.set(getDesktopLODDecreaseFPS()); hmdLODDecreaseFPS.set(getHMDLODDecreaseFPS()); - boundaryLevelAdjust.set(getBoundaryLevelAdjust()); - octreeSizeScale.set(getOctreeSizeScale()); } diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index 97991c62a4..378a1391f4 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -46,9 +46,9 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : _feedback->setFixedWidth(FEEDBACK_WIDTH); form->addRow("You can see... ", _feedback); - form->addRow("Automatic LOD Adjustment:", _automaticLODAdjust = new QCheckBox(this)); - _automaticLODAdjust->setChecked(lodManager->getAutomaticLODAdjust()); - connect(_automaticLODAdjust, SIGNAL(toggled(bool)), SLOT(updateAutomaticLODAdjust())); + form->addRow("Manually Adjust Level of Detail:", _manualLODAdjust = new QCheckBox(this)); + _manualLODAdjust->setChecked(!lodManager->getAutomaticLODAdjust()); + connect(_manualLODAdjust, SIGNAL(toggled(bool)), SLOT(updateAutomaticLODAdjust())); _lodSize = new QSlider(Qt::Horizontal, this); const int MAX_LOD_SIZE = MAX_LOD_SIZE_MULTIPLIER; @@ -65,7 +65,7 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) : _lodSize->setPageStep(PAGE_STEP_LOD_SIZE); int sliderValue = lodManager->getOctreeSizeScale() / TREE_SCALE; _lodSize->setValue(sliderValue); - form->addRow("Non-Avatar Content LOD:", _lodSize); + form->addRow("Level of Detail:", _lodSize); connect(_lodSize,SIGNAL(valueChanged(int)),this,SLOT(sizeScaleValueChanged(int))); // Add a button to reset @@ -86,12 +86,8 @@ void LodToolsDialog::reloadSliders() { void LodToolsDialog::updateAutomaticLODAdjust() { auto lodManager = DependencyManager::get(); - lodManager->setAutomaticLODAdjust(_automaticLODAdjust->isChecked()); -} - -void LodToolsDialog::updateLODValues() { - auto lodManager = DependencyManager::get(); - lodManager->setAutomaticLODAdjust(_automaticLODAdjust->isChecked()); + lodManager->setAutomaticLODAdjust(!_manualLODAdjust->isChecked()); + _lodSize->setEnabled(_manualLODAdjust->isChecked()); } void LodToolsDialog::sizeScaleValueChanged(int value) { @@ -106,9 +102,9 @@ void LodToolsDialog::resetClicked(bool checked) { int sliderValue = DEFAULT_OCTREE_SIZE_SCALE / TREE_SCALE; _lodSize->setValue(sliderValue); - _automaticLODAdjust->setChecked(true); + _manualLODAdjust->setChecked(false); - updateLODValues(); // tell our LOD manager about the reset + updateAutomaticLODAdjust(); // tell our LOD manager about the reset } void LodToolsDialog::reject() { @@ -119,6 +115,15 @@ void LodToolsDialog::reject() { void LodToolsDialog::closeEvent(QCloseEvent* event) { this->QDialog::closeEvent(event); emit closed(); + auto lodManager = DependencyManager::get(); + + // always revert back to automatic LOD adjustment when closed + lodManager->setAutomaticLODAdjust(true); + + // if the user adjusted the LOD above "normal" then always revert back to default + if (lodManager->getOctreeSizeScale() > DEFAULT_OCTREE_SIZE_SCALE) { + lodManager->setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE); + } } diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index 91be04e0ed..e5a2dae836 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -34,7 +34,6 @@ public slots: void resetClicked(bool checked); void reloadSliders(); void updateAutomaticLODAdjust(); - void updateLODValues(); protected: @@ -44,7 +43,7 @@ protected: private: QSlider* _lodSize; - QCheckBox* _automaticLODAdjust; + QCheckBox* _manualLODAdjust; QDoubleSpinBox* _desktopLODDecreaseFPS; From 0e441261069f5b009931ec4c9d8ddaa7dbe0e74a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Mar 2015 16:38:13 -0700 Subject: [PATCH 26/26] added LOD Warnings to notifications.js --- examples/defaultScripts.js | 1 - examples/hmdDefaults.js | 1 - examples/notifications.js | 36 ++++++++-- examples/utilities/LODWarning.js | 115 ------------------------------- 4 files changed, 31 insertions(+), 122 deletions(-) delete mode 100644 examples/utilities/LODWarning.js diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index f52d6be87e..05ffb0bd3f 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -18,4 +18,3 @@ Script.load("lobby.js"); Script.load("notifications.js"); Script.load("look.js"); Script.load("users.js"); -Script.load("utilities/LODWarning.js"); diff --git a/examples/hmdDefaults.js b/examples/hmdDefaults.js index 1e96d41713..0096b11777 100644 --- a/examples/hmdDefaults.js +++ b/examples/hmdDefaults.js @@ -13,5 +13,4 @@ Script.load("progress.js"); Script.load("lobby.js"); Script.load("notifications.js"); Script.load("controllers/oculus/goTo.js"); -Script.load("utilities/LODWarning.js"); //Script.load("scripts.js"); // Not created yet diff --git a/examples/notifications.js b/examples/notifications.js index 0c2a06c878..7e56c1b50d 100644 --- a/examples/notifications.js +++ b/examples/notifications.js @@ -43,7 +43,6 @@ // after that we will send it to createNotification(text). // If the message is 42 chars or less you should bypass wordWrap() and call createNotification() directly. - // To add a keypress driven notification: // // 1. Add a key to the keyPressEvent(key). @@ -85,16 +84,19 @@ var PLAY_NOTIFICATION_SOUNDS_MENU_ITEM = "Play Notification Sounds"; var NOTIFICATION_MENU_ITEM_POST = " Notifications"; var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds"; var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_"; +var lodTextID = false; var NotificationType = { UNKNOWN: 0, MUTE_TOGGLE: 1, SNAPSHOT: 2, WINDOW_RESIZE: 3, + LOD_WARNING: 4, properties: [ { text: "Mute Toggle" }, { text: "Snapshot" }, - { text: "Window Resize" } + { text: "Window Resize" }, + { text: "Level of Detail" } ], getTypeFromMenuItem: function(menuItemName) { if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) { @@ -143,6 +145,10 @@ function createArrays(notice, button, createTime, height, myAlpha) { // This handles the final dismissal of a notification after fading function dismiss(firstNoteOut, firstButOut, firstOut) { + if (firstNoteOut == lodTextID) { + lodTextID = false; + } + Overlays.deleteOverlay(firstNoteOut); Overlays.deleteOverlay(firstButOut); notifications.splice(firstOut, 1); @@ -261,7 +267,8 @@ function notify(notice, button, height) { height: noticeHeight }); } else { - notifications.push((Overlays.addOverlay("text", notice))); + var notificationText = Overlays.addOverlay("text", notice); + notifications.push((notificationText)); buttons.push((Overlays.addOverlay("image", button))); } @@ -272,6 +279,7 @@ function notify(notice, button, height) { last = notifications.length - 1; createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]); fadeIn(notifications[last], buttons[last]); + return notificationText; } // This function creates and sizes the overlays @@ -331,11 +339,15 @@ function createNotification(text, notificationType) { randomSounds.playRandom(); } - notify(noticeProperties, buttonProperties, height); + return notify(noticeProperties, buttonProperties, height); } function deleteNotification(index) { - Overlays.deleteOverlay(notifications[index]); + var notificationTextID = notifications[index]; + if (notificationTextID == lodTextID) { + lodTextID = false; + } + Overlays.deleteOverlay(notificationTextID); Overlays.deleteOverlay(buttons[index]); notifications.splice(index, 1); buttons.splice(index, 1); @@ -575,6 +587,20 @@ function menuItemEvent(menuItem) { } } +LODManager.LODDecreased.connect(function() { + var warningText = "\n" + + "Due to the complexity of the content, the \n" + + "level of detail has been decreased." + + "You can now see: \n" + + LODManager.getLODFeedbackText(); + + if (lodTextID == false) { + lodTextID = createNotification(warningText, NotificationType.LOD_WARNING); + } else { + Overlays.editOverlay(lodTextID, { text: warningText }); + } +}); + AudioDevice.muteToggled.connect(onMuteStateChanged); Controller.keyPressEvent.connect(keyPressEvent); Controller.mousePressEvent.connect(mousePressEvent); diff --git a/examples/utilities/LODWarning.js b/examples/utilities/LODWarning.js deleted file mode 100644 index 644d98ebf4..0000000000 --- a/examples/utilities/LODWarning.js +++ /dev/null @@ -1,115 +0,0 @@ -// LODWarning.js -// examples -// -// Created by Brad Hefta-Gaub on 3/17/15. -// Copyright 2015 High Fidelity, Inc. -// -// This script will display a warning when the LOD is adjusted to do scene complexity. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var DISPLAY_WARNING_FOR = 3; // in seconds -var DISTANCE_FROM_CAMERA = 2; -var SHOW_LOD_UP_MESSAGE = false; // By default we only display the LOD message when reducing LOD - - -var warningIsVisible = false; // initially the warning is hidden -var warningShownAt = 0; -var billboardPosition = Vec3.sum(Camera.getPosition(), - Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation()))); - -var warningOverlay = Overlays.addOverlay("text3d", { - position: billboardPosition, - dimensions: { x: 2, y: 1.25 }, - width: 2, - height: 1.25, - backgroundColor: { red: 0, green: 0, blue: 0 }, - color: { red: 255, green: 255, blue: 255}, - topMargin: 0.1, - leftMargin: 0.1, - lineHeight: 0.07, - text: "", - alpha: 0.5, - backgroundAlpha: 0.7, - isFacingAvatar: true, - visible: warningIsVisible, - }); - -// Handle moving the billboard to remain in front of the camera -var billboardNeedsMoving = false; -Script.update.connect(function() { - - if (warningIsVisible) { - var bestBillboardPosition = Vec3.sum(Camera.getPosition(), - Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation()))); - - var MAX_DISTANCE = 0.5; - var CLOSE_ENOUGH = 0.01; - if (!billboardNeedsMoving && Vec3.distance(bestBillboardPosition, billboardPosition) > MAX_DISTANCE) { - billboardNeedsMoving = true; - } - - if (billboardNeedsMoving && Vec3.distance(bestBillboardPosition, billboardPosition) <= CLOSE_ENOUGH) { - billboardNeedsMoving = false; - } - - if (billboardNeedsMoving) { - // slurp the billboard to the best location - moveVector = Vec3.multiply(0.05, Vec3.subtract(bestBillboardPosition, billboardPosition)); - billboardPosition = Vec3.sum(billboardPosition, moveVector); - Overlays.editOverlay(warningOverlay, { position: billboardPosition }); - } - - var now = new Date(); - var sinceWarningShown = now - warningShownAt; - if (sinceWarningShown > 1000 * DISPLAY_WARNING_FOR) { - warningIsVisible = false; - Overlays.editOverlay(warningOverlay, { visible: warningIsVisible }); - } - } -}); - -LODManager.LODIncreased.connect(function() { - if (SHOW_LOD_UP_MESSAGE) { - // if the warning wasn't visible, then move it before showing it. - if (!warningIsVisible) { - billboardPosition = Vec3.sum(Camera.getPosition(), - Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation()))); - Overlays.editOverlay(warningOverlay, { position: billboardPosition }); - } - - warningShownAt = new Date(); - warningIsVisible = true; - warningText = "Level of detail has been increased. \n" - + "You can now see: \n" - + LODManager.getLODFeedbackText(); - - Overlays.editOverlay(warningOverlay, { visible: warningIsVisible, text: warningText }); - } -}); - -LODManager.LODDecreased.connect(function() { - // if the warning wasn't visible, then move it before showing it. - if (!warningIsVisible) { - billboardPosition = Vec3.sum(Camera.getPosition(), - Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation()))); - Overlays.editOverlay(warningOverlay, { position: billboardPosition }); - } - - warningShownAt = new Date(); - warningIsVisible = true; - warningText = "\n" - + "Due to the complexity of the content, the \n" - + "level of detail has been decreased. \n" - + "You can now see: \n" - + LODManager.getLODFeedbackText(); - - Overlays.editOverlay(warningOverlay, { visible: warningIsVisible, text: warningText }); -}); - - -Script.scriptEnding.connect(function() { - Overlays.deleteOverlay(warningOverlay); -}); \ No newline at end of file