Merge branch 'master' of https://github.com/worklist/hifi into 19597

This commit is contained in:
Stojce Slavkovski 2014-04-24 22:48:11 +02:00
commit 8f2878953c
23 changed files with 596 additions and 153 deletions

View file

@ -449,7 +449,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
if (nodeInterestList.size() > 0) {
DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
unsigned int dataMTU = dtlsSession ? gnutls_dtls_get_data_mtu(*dtlsSession->getGnuTLSSession()) : MAX_PACKET_SIZE;
int dataMTU = dtlsSession ? (int)gnutls_dtls_get_data_mtu(*dtlsSession->getGnuTLSSession()) : MAX_PACKET_SIZE;
// if the node has any interest types, send back those nodes as well
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {

View file

@ -480,7 +480,7 @@ void Audio::handleAudioInput() {
float thisSample = 0;
int samplesOverNoiseGate = 0;
const float NOISE_GATE_HEIGHT = 7.f;
const float NOISE_GATE_HEIGHT = 7.0f;
const int NOISE_GATE_WIDTH = 5;
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
@ -490,7 +490,7 @@ void Audio::handleAudioInput() {
//
// Check clipping, adjust DC offset, and check if should open noise gate
//
float measuredDcOffset = 0.f;
float measuredDcOffset = 0.0f;
// Increment the time since the last clip
if (_timeSinceLastClip >= 0.0f) {
_timeSinceLastClip += (float) NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float) SAMPLE_RATE;
@ -500,7 +500,7 @@ void Audio::handleAudioInput() {
measuredDcOffset += monoAudioSamples[i];
monoAudioSamples[i] -= (int16_t) _dcOffset;
thisSample = fabsf(monoAudioSamples[i]);
if (thisSample >= (32767.f * CLIPPING_THRESHOLD)) {
if (thisSample >= (32767.0f * CLIPPING_THRESHOLD)) {
_timeSinceLastClip = 0.0f;
}
loudness += thisSample;
@ -511,18 +511,18 @@ void Audio::handleAudioInput() {
}
measuredDcOffset /= NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL;
if (_dcOffset == 0.f) {
if (_dcOffset == 0.0f) {
// On first frame, copy over measured offset
_dcOffset = measuredDcOffset;
} else {
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.f - DC_OFFSET_AVERAGING) * measuredDcOffset;
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
}
// Add tone injection if enabled
const float TONE_FREQ = 220.f / SAMPLE_RATE * TWO_PI;
const float QUARTER_VOLUME = 8192.f;
const float TONE_FREQ = 220.0f / SAMPLE_RATE * TWO_PI;
const float QUARTER_VOLUME = 8192.0f;
if (_toneInjectionEnabled) {
loudness = 0.f;
loudness = 0.0f;
for (int i = 0; i < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) {
monoAudioSamples[i] = QUARTER_VOLUME * sinf(TONE_FREQ * (float)(i + _proceduralEffectSample));
loudness += fabsf(monoAudioSamples[i]);
@ -532,7 +532,7 @@ void Audio::handleAudioInput() {
// If Noise Gate is enabled, check and turn the gate on and off
if (!_toneInjectionEnabled && _noiseGateEnabled) {
float averageOfAllSampleFrames = 0.f;
float averageOfAllSampleFrames = 0.0f;
_noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness;
if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
float smallestSample = FLT_MAX;
@ -659,9 +659,9 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
_stdev.reset();
// Set jitter buffer to be a multiple of the measured standard deviation
const int MAX_JITTER_BUFFER_SAMPLES = _ringBuffer.getSampleCapacity() / 2;
const float NUM_STANDARD_DEVIATIONS = 3.f;
const float NUM_STANDARD_DEVIATIONS = 3.0f;
if (Menu::getInstance()->getAudioJitterBufferSamples() == 0) {
float newJitterBufferSamples = (NUM_STANDARD_DEVIATIONS * _measuredJitter) / 1000.f * SAMPLE_RATE;
float newJitterBufferSamples = (NUM_STANDARD_DEVIATIONS * _measuredJitter) / 1000.0f * SAMPLE_RATE;
setJitterBufferSamples(glm::clamp((int)newJitterBufferSamples, 0, MAX_JITTER_BUFFER_SAMPLES));
}
}
@ -900,10 +900,10 @@ void Audio::toggleAudioSpatialProcessing() {
void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) {
float sample;
const float COLLISION_SOUND_CUTOFF_LEVEL = 0.01f;
const float COLLISION_SOUND_MAX_VOLUME = 1000.f;
const float COLLISION_SOUND_MAX_VOLUME = 1000.0f;
const float UP_MAJOR_FIFTH = powf(1.5f, 4.0f);
const float DOWN_TWO_OCTAVES = 4.f;
const float DOWN_FOUR_OCTAVES = 16.f;
const float DOWN_TWO_OCTAVES = 4.0f;
const float DOWN_FOUR_OCTAVES = 16.0f;
float t;
if (_collisionSoundMagnitude > COLLISION_SOUND_CUTOFF_LEVEL) {
for (int i = 0; i < numSamples; i++) {
@ -933,12 +933,12 @@ void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) {
_proceduralEffectSample += numSamples;
// Add a drum sound
const float MAX_VOLUME = 32000.f;
const float MAX_DURATION = 2.f;
const float MAX_VOLUME = 32000.0f;
const float MAX_DURATION = 2.0f;
const float MIN_AUDIBLE_VOLUME = 0.001f;
const float NOISE_MAGNITUDE = 0.02f;
float frequency = (_drumSoundFrequency / SAMPLE_RATE) * TWO_PI;
if (_drumSoundVolume > 0.f) {
if (_drumSoundVolume > 0.0f) {
for (int i = 0; i < numSamples; i++) {
t = (float) _drumSoundSample + (float) i;
sample = sinf(t * frequency);
@ -958,12 +958,12 @@ void Audio::addProceduralSounds(int16_t* monoInput, int numSamples) {
_localProceduralSamples[i] = glm::clamp(_localProceduralSamples[i] + collisionSample,
MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE);
_drumSoundVolume *= (1.f - _drumSoundDecay);
_drumSoundVolume *= (1.0f - _drumSoundDecay);
}
_drumSoundSample += numSamples;
_drumSoundDuration = glm::clamp(_drumSoundDuration - (AUDIO_CALLBACK_MSECS / 1000.f), 0.f, MAX_DURATION);
if (_drumSoundDuration == 0.f || (_drumSoundVolume < MIN_AUDIBLE_VOLUME)) {
_drumSoundVolume = 0.f;
_drumSoundDuration = glm::clamp(_drumSoundDuration - (AUDIO_CALLBACK_MSECS / 1000.0f), 0.0f, MAX_DURATION);
if (_drumSoundDuration == 0.0f || (_drumSoundVolume < MIN_AUDIBLE_VOLUME)) {
_drumSoundVolume = 0.0f;
}
}
}
@ -996,7 +996,7 @@ void Audio::renderToolBox(int x, int y, bool boxed) {
if (boxed) {
bool isClipping = ((getTimeSinceLastClip() > 0.f) && (getTimeSinceLastClip() < 1.f));
bool isClipping = ((getTimeSinceLastClip() > 0.0f) && (getTimeSinceLastClip() < 1.0f));
const int BOX_LEFT_PADDING = 5;
const int BOX_TOP_PADDING = 10;
const int BOX_WIDTH = 266;
@ -1007,9 +1007,9 @@ void Audio::renderToolBox(int x, int y, bool boxed) {
glBindTexture(GL_TEXTURE_2D, _boxTextureId);
if (isClipping) {
glColor3f(1.f,0.f,0.f);
glColor3f(1.0f, 0.0f, 0.0f);
} else {
glColor3f(.41f,.41f,.41f);
glColor3f(0.41f, 0.41f, 0.41f);
}
glBegin(GL_QUADS);
@ -1086,10 +1086,8 @@ void Audio::addBufferToScope(
// Short int pointer to mapped samples in byte array
int16_t* destination = (int16_t*) byteArray.data();
for (int i = 0; i < NETWORK_SAMPLES_PER_FRAME; i++) {
for (unsigned int i = 0; i < NETWORK_SAMPLES_PER_FRAME; i++) {
sample = (float)source[i * sourceNumberOfChannels + sourceChannel];
if (sample > 0) {
value = (int16_t)(multiplier * logf(sample));
} else if (sample < 0) {
@ -1097,7 +1095,6 @@ void Audio::addBufferToScope(
} else {
value = 0;
}
destination[i + frameOffset] = value;
}
}

View file

@ -335,6 +335,8 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandsCollideWithSelf, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, true);
addDisabledActionAndSeparator(developerMenu, "Testing");

View file

@ -256,6 +256,7 @@ private:
namespace MenuOption {
const QString AboutApp = "About Interface";
const QString AlignForearmsWithWrists = "Align Forearms with Wrists";
const QString AmbientOcclusion = "Ambient Occlusion";
const QString Atmosphere = "Atmosphere";
const QString AudioNoiseReduction = "Audio Noise Reduction";
@ -354,6 +355,7 @@ namespace MenuOption {
const QString SettingsImport = "Import Settings";
const QString Shadows = "Shadows";
const QString ShowCulledSharedFaces = "Show Culled Shared Voxel Faces";
const QString ShowIKConstraints = "Show IK Constraints";
const QString Stars = "Stars";
const QString Stats = "Stats";
const QString StopAllScripts = "Stop All Scripts";

View file

@ -335,15 +335,17 @@ void MyAvatar::simulate(float deltaTime) {
radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f));
radius *= COLLISION_RADIUS_SCALAR;
}
if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
updateCollisionWithEnvironment(deltaTime, radius);
}
if (_collisionFlags & COLLISION_GROUP_VOXELS) {
updateCollisionWithVoxels(deltaTime, radius);
}
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
updateCollisionWithAvatars(deltaTime);
if (_collisionFlags) {
updateShapePositions();
if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) {
updateCollisionWithEnvironment(deltaTime, radius);
}
if (_collisionFlags & COLLISION_GROUP_VOXELS) {
updateCollisionWithVoxels(deltaTime, radius);
}
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
updateCollisionWithAvatars(deltaTime);
}
}
}
@ -460,6 +462,9 @@ void MyAvatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
return; // exit early
}
Avatar::render(cameraPosition, renderMode);
if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints)) {
_skeletonModel.renderIKConstraints();
}
}
void MyAvatar::renderHeadMouse() const {
@ -795,19 +800,21 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
}
}
static CollisionList myCollisions(64);
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
const float VOXEL_ELASTICITY = 0.4f;
const float VOXEL_DAMPING = 0.0f;
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
glm::vec3 penetration;
float pelvisFloatingHeight = getPelvisFloatingHeight();
if (Application::getInstance()->getVoxelTree()->findCapsulePenetration(
_position - glm::vec3(0.0f, pelvisFloatingHeight - radius, 0.0f),
_position + glm::vec3(0.0f, getSkeletonHeight() - pelvisFloatingHeight + radius, 0.0f), radius, penetration)) {
_lastCollisionPosition = _position;
updateCollisionSound(penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
applyHardCollision(penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
myCollisions.clear();
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions)) {
const float VOXEL_ELASTICITY = 0.4f;
const float VOXEL_DAMPING = 0.0f;
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
}
}
void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) {
@ -912,7 +919,6 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
// no need to compute a bunch of stuff if we have one or fewer avatars
return;
}
updateShapePositions();
float myBoundingRadius = getBoundingRadius();
const float BODY_COLLISION_RESOLUTION_FACTOR = glm::max(1.0f, deltaTime / BODY_COLLISION_RESOLUTION_TIMESCALE);

View file

@ -103,6 +103,11 @@ void SkeletonModel::getBodyShapes(QVector<const Shape*>& shapes) const {
shapes.push_back(&_boundingShape);
}
void SkeletonModel::renderIKConstraints() {
renderJointConstraints(getRightHandJointIndex());
renderJointConstraints(getLeftHandJointIndex());
}
class IndexValue {
public:
int index;
@ -133,7 +138,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
// align hand with forearm
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), false);
applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector));
}
void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJointIndices,
@ -148,12 +153,15 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
return;
}
// rotate forearm to align with palm direction
// rotate palm to align with palm direction
glm::quat palmRotation;
getJointRotation(parentJointIndex, palmRotation, true);
applyRotationDelta(parentJointIndex, rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()), false);
getJointRotation(parentJointIndex, palmRotation, true);
if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
getJointRotation(parentJointIndex, palmRotation, true);
} else {
getJointRotation(jointIndex, palmRotation, true);
}
palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()) * palmRotation;
// sort the finger indices by raw x, get the average direction
QVector<IndexValue> fingerIndices;
glm::vec3 direction;
@ -173,17 +181,20 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
float directionLength = glm::length(direction);
const unsigned int MIN_ROTATION_FINGERS = 3;
if (directionLength > EPSILON && palm.getNumFingers() >= MIN_ROTATION_FINGERS) {
applyRotationDelta(parentJointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction), false);
getJointRotation(parentJointIndex, palmRotation, true);
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction) * palmRotation;
}
// let wrist inherit forearm rotation
_jointStates[jointIndex].rotation = glm::quat();
// set elbow position from wrist position
glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f);
setJointPosition(parentJointIndex, palm.getPosition() + forearmVector *
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale));
// set hand position, rotation
if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f);
setJointPosition(parentJointIndex, palm.getPosition() + forearmVector *
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale));
setJointRotation(parentJointIndex, palmRotation, true);
_jointStates[jointIndex].rotation = glm::quat();
} else {
setJointPosition(jointIndex, palm.getPosition(), palmRotation, true);
}
}
void SkeletonModel::updateJointState(int index) {
@ -210,3 +221,59 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
glm::normalize(inverse * axes[0])) * joint.rotation;
}
void SkeletonModel::renderJointConstraints(int jointIndex) {
if (jointIndex == -1) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const float BASE_DIRECTION_SIZE = 300.0f;
float directionSize = BASE_DIRECTION_SIZE * extractUniformScale(_scale);
glLineWidth(3.0f);
do {
const FBXJoint& joint = geometry.joints.at(jointIndex);
const JointState& jointState = _jointStates.at(jointIndex);
glm::vec3 position = extractTranslation(jointState.transform) + _translation;
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _jointStates.at(joint.parentIndex).combinedRotation;
glm::vec3 rotationAxis = glm::axis(parentRotation);
glRotatef(glm::degrees(glm::angle(parentRotation)), rotationAxis.x, rotationAxis.y, rotationAxis.z);
float fanScale = directionSize * 0.75f;
glScalef(fanScale, fanScale, fanScale);
const int AXIS_COUNT = 3;
for (int i = 0; i < AXIS_COUNT; i++) {
if (joint.rotationMin[i] <= -PI + EPSILON && joint.rotationMax[i] >= PI - EPSILON) {
continue; // unconstrained
}
glm::vec3 axis;
axis[i] = 1.0f;
glm::vec3 otherAxis;
if (i == 0) {
otherAxis.y = 1.0f;
} else {
otherAxis.x = 1.0f;
}
glColor4f(otherAxis.r, otherAxis.g, otherAxis.b, 0.75f);
glBegin(GL_TRIANGLE_FAN);
glVertex3f(0.0f, 0.0f, 0.0f);
const int FAN_SEGMENTS = 16;
for (int j = 0; j < FAN_SEGMENTS; j++) {
glm::vec3 rotated = glm::angleAxis(glm::mix(joint.rotationMin[i], joint.rotationMax[i],
(float)j / (FAN_SEGMENTS - 1)), axis) * otherAxis;
glVertex3f(rotated.x, rotated.y, rotated.z);
}
glEnd();
}
glPopMatrix();
renderOrientationDirections(position, jointState.combinedRotation, directionSize);
jointIndex = joint.parentIndex;
} while (jointIndex != -1 && geometry.joints.at(jointIndex).isFree);
glLineWidth(1.0f);
}

View file

@ -33,6 +33,8 @@ public:
/// \param shapes[out] list of shapes for body collisions
void getBodyShapes(QVector<const Shape*>& shapes) const;
void renderIKConstraints();
protected:
void applyHandPosition(int jointIndex, const glm::vec3& position);
@ -46,6 +48,8 @@ protected:
virtual void maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
private:
void renderJointConstraints(int jointIndex);
Avatar* _owningAvatar;
};

View file

@ -882,12 +882,12 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind)
return true;
}
bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int lastFreeIndex,
bool allIntermediatesFree, const glm::vec3& alignment) {
bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation,
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return false;
}
glm::vec3 relativePosition = position - _translation;
glm::vec3 relativePosition = translation - _translation;
const FBXGeometry& geometry = _geometry->getFBXGeometry();
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
if (freeLineage.isEmpty()) {
@ -896,13 +896,21 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int last
if (lastFreeIndex == -1) {
lastFreeIndex = freeLineage.last();
}
// this is a cyclic coordinate descent algorithm: see
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
const int ITERATION_COUNT = 1;
glm::vec3 worldAlignment = _rotation * alignment;
for (int i = 0; i < ITERATION_COUNT; i++) {
// first, we go from the joint upwards, rotating the end as close as possible to the target
// first, try to rotate the end effector as close as possible to the target rotation, if any
glm::quat endRotation;
if (useRotation) {
getJointRotation(jointIndex, endRotation, true);
applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation));
getJointRotation(jointIndex, endRotation, true);
}
// then, we go from the joint upwards, rotating the end as close as possible to the target
glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].transform);
for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) {
int index = freeLineage.at(j);
@ -914,8 +922,17 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int last
glm::vec3 jointPosition = extractTranslation(state.transform);
glm::vec3 jointVector = endPosition - jointPosition;
glm::quat oldCombinedRotation = state.combinedRotation;
applyRotationDelta(index, rotationBetween(jointVector, relativePosition - jointPosition));
endPosition = state.combinedRotation * glm::inverse(oldCombinedRotation) * jointVector + jointPosition;
glm::quat combinedDelta;
float combinedWeight;
if (useRotation) {
combinedDelta = safeMix(rotation * glm::inverse(endRotation),
rotationBetween(jointVector, relativePosition - jointPosition), 0.5f);
combinedWeight = 2.0f;
} else {
combinedDelta = rotationBetween(jointVector, relativePosition - jointPosition);
combinedWeight = 1.0f;
}
if (alignment != glm::vec3() && j > 1) {
jointVector = endPosition - jointPosition;
glm::vec3 positionSum;
@ -929,9 +946,16 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, int last
glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector));
const float LENGTH_EPSILON = 0.001f;
if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) {
applyRotationDelta(index, rotationBetween(projectedCenterOfMass, projectedAlignment));
combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment),
1.0f / (combinedWeight + 1.0f));
}
}
applyRotationDelta(index, combinedDelta);
glm::quat actualDelta = state.combinedRotation * glm::inverse(oldCombinedRotation);
endPosition = actualDelta * jointVector + jointPosition;
if (useRotation) {
endRotation = actualDelta * endRotation;
}
}
}
@ -1012,8 +1036,9 @@ void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool cons
state.combinedRotation = delta * state.combinedRotation;
return;
}
glm::quat newRotation = glm::quat(glm::clamp(safeEulerAngles(state.rotation *
glm::inverse(state.combinedRotation) * delta * state.combinedRotation), joint.rotationMin, joint.rotationMax));
glm::quat targetRotation = delta * state.combinedRotation;
glm::vec3 eulers = safeEulerAngles(state.rotation * glm::inverse(state.combinedRotation) * targetRotation);
glm::quat newRotation = glm::quat(glm::clamp(eulers, joint.rotationMin, joint.rotationMax));
state.combinedRotation = state.combinedRotation * glm::inverse(state.rotation) * newRotation;
state.rotation = newRotation;
}
@ -1141,7 +1166,7 @@ void Model::applyCollision(CollisionInfo& collision) {
getJointPosition(jointIndex, end);
glm::vec3 newEnd = start + glm::angleAxis(angle, axis) * (end - start);
// try to move it
setJointPosition(jointIndex, newEnd, -1, true);
setJointPosition(jointIndex, newEnd, glm::quat(), false, -1, true);
}
}
}

View file

@ -193,6 +193,8 @@ public:
/// Sets blended vertices computed in a separate thread.
void setBlendedVertices(const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
const CapsuleShape& getBoundingShape() const { return _boundingShape; }
protected:
QSharedPointer<NetworkGeometry> _geometry;
@ -240,8 +242,9 @@ protected:
bool getJointPosition(int jointIndex, glm::vec3& position) const;
bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const;
bool setJointPosition(int jointIndex, const glm::vec3& position, int lastFreeIndex = -1,
bool allIntermediatesFree = false, const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f));
bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(),
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f));
bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false);
void setJointTranslation(int jointIndex, const glm::vec3& translation);

View file

@ -39,7 +39,8 @@ ScriptEditorWidget::ScriptEditorWidget() :
setTitleBarWidget(new QWidget());
QFontMetrics fm(_scriptEditorWidgetUI->scriptEdit->font());
_scriptEditorWidgetUI->scriptEdit->setTabStopWidth(fm.width('0') * 4);
ScriptHighlighting* highlighting = new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document());
// We create a new ScriptHighligting QObject and provide it with a parent so this is NOT a memory leak.
new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document());
QTimer::singleShot(0, _scriptEditorWidgetUI->scriptEdit, SLOT(setFocus()));
}

View file

@ -20,18 +20,20 @@
#include <QDebug>
#include "CoverageMap.h"
#include <GeometryUtil.h>
#include "OctalCode.h"
#include <OctalCode.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <Shape.h>
#include <ShapeCollider.h>
//#include "Tags.h"
#include "ViewFrustum.h"
#include "CoverageMap.h"
#include "OctreeConstants.h"
#include "OctreeElementBag.h"
#include "Octree.h"
#include "ViewFrustum.h"
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) {
return voxelSizeScale / powf(2, renderLevel);
@ -676,6 +678,13 @@ public:
bool found;
};
class ShapeArgs {
public:
const Shape* shape;
CollisionList& collisions;
bool found;
};
bool findCapsulePenetrationOp(OctreeElement* node, void* extraData) {
CapsuleArgs* args = static_cast<CapsuleArgs*>(extraData);
@ -697,6 +706,27 @@ bool findCapsulePenetrationOp(OctreeElement* node, void* extraData) {
return false;
}
bool findShapeCollisionsOp(OctreeElement* node, void* extraData) {
ShapeArgs* args = static_cast<ShapeArgs*>(extraData);
// coarse check against bounds
AABox cube = node->getAABox();
cube.scale(TREE_SCALE);
if (!cube.expandedContains(args->shape->getPosition(), args->shape->getBoundingRadius())) {
return false;
}
if (!node->isLeaf()) {
return true; // recurse on children
}
if (node->hasContent()) {
if (ShapeCollider::collideShapeWithAACube(args->shape, cube.calcCenter(), cube.getScale(), args->collisions)) {
args->found = true;
return true;
}
}
return false;
}
bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
glm::vec3& penetration, Octree::lockType lockType) {
@ -727,6 +757,29 @@ bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end
return args.found;
}
bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions, Octree::lockType lockType) {
ShapeArgs args = { shape, collisions, false };
bool gotLock = false;
if (lockType == Octree::Lock) {
lockForRead();
gotLock = true;
} else if (lockType == Octree::TryLock) {
gotLock = tryLockForRead();
if (!gotLock) {
return args.found; // if we wanted to tryLock, and we couldn't then just bail...
}
}
recurseTreeWithOperation(findShapeCollisionsOp, &args);
if (gotLock) {
unlock();
}
return args.found;
}
class GetElementEnclosingArgs {
public:
OctreeElement* element;

View file

@ -21,6 +21,7 @@ class Octree;
class OctreeElement;
class OctreeElementBag;
class OctreePacketData;
class Shape;
#include "JurisdictionMap.h"
@ -30,6 +31,8 @@ class OctreePacketData;
#include "OctreePacketData.h"
#include "OctreeSceneStats.h"
#include <CollisionInfo.h>
#include <QObject>
#include <QReadWriteLock>
@ -246,6 +249,8 @@ public:
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
glm::vec3& penetration, Octree::lockType lockType = Octree::TryLock);
bool findShapeCollisions(const Shape* shape, CollisionList& collisions, Octree::lockType = Octree::TryLock);
OctreeElement* getElementEnclosingPoint(const glm::vec3& point, Octree::lockType lockType = Octree::TryLock);
// Note: this assumes the fileFormat is the HIO individual voxels code files

View file

@ -21,10 +21,10 @@
#include "AABox.h"
#include "OctalCode.h"
#include "SharedUtil.h"
#include "OctreeConstants.h"
#include "OctreeElement.h"
#include "Octree.h"
#include "SharedUtil.h"
quint64 OctreeElement::_voxelMemoryUsage = 0;
quint64 OctreeElement::_octcodeMemoryUsage = 0;

View file

@ -19,6 +19,7 @@
#include <QReadWriteLock>
#include <SharedUtil.h>
#include "AABox.h"
#include "ViewFrustum.h"
#include "OctreeConstants.h"

View file

@ -19,7 +19,6 @@
#include "AABox.h"
#include "Plane.h"
#include "OctreeConstants.h"
#include "OctreeProjectedPolygon.h"

View file

@ -48,3 +48,6 @@ void CollisionList::clear() {
_size = 0;
}
CollisionInfo* CollisionList::operator[](int index) {
return (index > -1 && index < _size) ? &(_collisions[index]) : NULL;
}

View file

@ -95,6 +95,8 @@ public:
/// Clear valid collisions.
void clear();
CollisionInfo* operator[](int index);
private:
int _maxSize; // the container cannot get larger than this
int _size; // the current number of valid collisions in the list

View file

@ -92,6 +92,29 @@ bool collideShapesCoarse(const QVector<const Shape*>& shapesA, const QVector<con
return false;
}
bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) {
int typeA = shapeA->getType();
if (typeA == Shape::SPHERE_SHAPE) {
return sphereAACube(static_cast<const SphereShape*>(shapeA), cubeCenter, cubeSide, collisions);
} else if (typeA == Shape::CAPSULE_SHAPE) {
return capsuleAACube(static_cast<const CapsuleShape*>(shapeA), cubeCenter, cubeSide, collisions);
} else if (typeA == Shape::LIST_SHAPE) {
const ListShape* listA = static_cast<const ListShape*>(shapeA);
bool touching = false;
for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) {
const Shape* subShape = listA->getSubShape(i);
int subType = subShape->getType();
if (subType == Shape::SPHERE_SHAPE) {
touching = sphereAACube(static_cast<const SphereShape*>(subShape), cubeCenter, cubeSide, collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = capsuleAACube(static_cast<const CapsuleShape*>(subShape), cubeCenter, cubeSide, collisions) || touching;
}
}
return touching;
}
return false;
}
bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions) {
glm::vec3 BA = sphereB->getPosition() - sphereA->getPosition();
float distanceSquared = glm::dot(BA, BA);
@ -567,4 +590,103 @@ bool listList(const ListShape* listA, const ListShape* listB, CollisionList& col
return touching;
}
// helper function
bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) {
glm::vec3 BA = cubeCenter - sphereCenter;
float distance = glm::length(BA);
if (distance > EPSILON) {
BA /= distance; // BA is now normalized
// compute the nearest point on sphere
glm::vec3 surfaceA = sphereCenter + sphereRadius * BA;
// compute the nearest point on cube
float maxBA = glm::max(glm::max(fabs(BA.x), fabs(BA.y)), fabs(BA.z));
glm::vec3 surfaceB = cubeCenter - (0.5f * cubeSide / maxBA) * BA;
// collision happens when "vector to surfaceA from surfaceB" dots with BA to produce a positive value
glm::vec3 surfaceAB = surfaceA - surfaceB;
if (glm::dot(surfaceAB, BA) > 0.f) {
CollisionInfo* collision = collisions.getNewCollision();
if (collision) {
/* KEEP THIS CODE -- this is how to collide the cube with stark face normals (no rounding).
* We might want to use this code later for sealing boundaries between adjacent voxels.
// penetration is parallel to box side direction
BA /= maxBA;
glm::vec3 direction;
glm::modf(BA, direction);
direction = glm::normalize(direction);
*/
// For rounded normals at edges and corners:
// At this point imagine that sphereCenter touches a "normalized" cube with rounded edges.
// This cube has a sidelength of 2 and its smoothing radius is sphereRadius/maxBA.
// We're going to try to compute the "negative normal" (and hence direction of penetration)
// of this surface.
float radius = sphereRadius / (distance * maxBA); // normalized radius
float shortLength = maxBA - radius;
glm::vec3 direction = BA;
if (shortLength > 0.0f) {
direction = glm::abs(BA) - glm::vec3(shortLength);
// Set any negative components to zero, and adopt the sign of the original BA component.
// Unfortunately there isn't an easy way to make this fast.
if (direction.x < 0.0f) {
direction.x = 0.f;
} else if (BA.x < 0.f) {
direction.x = -direction.x;
}
if (direction.y < 0.0f) {
direction.y = 0.f;
} else if (BA.y < 0.f) {
direction.y = -direction.y;
}
if (direction.z < 0.0f) {
direction.z = 0.f;
} else if (BA.z < 0.f) {
direction.z = -direction.z;
}
}
direction = glm::normalize(direction);
// penetration is the projection of surfaceAB on direction
collision->_penetration = glm::dot(surfaceAB, direction) * direction;
// contactPoint is on surface of A
collision->_contactPoint = sphereCenter - sphereRadius * direction;
return true;
}
}
} else if (sphereRadius + 0.5f * cubeSide > distance) {
// NOTE: for cocentric approximation we collide sphere and cube as two spheres which means
// this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON)
CollisionInfo* collision = collisions.getNewCollision();
if (collision) {
// the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis)
collision->_penetration = (sphereRadius + 0.5f * cubeSide) * glm::vec3(0.0f, -1.0f, 0.0f);
// contactPoint is on surface of A
collision->_contactPoint = sphereCenter + collision->_penetration;
return true;
}
}
return false;
}
bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) {
return sphereAACube(sphereA->getPosition(), sphereA->getRadius(), cubeCenter, cubeSide, collisions);
}
bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) {
// find nerest approach of capsule line segment to cube
glm::vec3 capsuleAxis;
capsuleA->computeNormalizedAxis(capsuleAxis);
float offset = glm::dot(cubeCenter - capsuleA->getPosition(), capsuleAxis);
float halfHeight = capsuleA->getHalfHeight();
if (offset > halfHeight) {
offset = halfHeight;
} else if (offset < -halfHeight) {
offset = -halfHeight;
}
glm::vec3 nearestApproach = capsuleA->getPosition() + offset * capsuleAxis;
// collide nearest approach like a sphere at that point
return sphereAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions);
}
} // namespace ShapeCollider

View file

@ -33,6 +33,13 @@ namespace ShapeCollider {
/// \return true if any shapes collide
bool collideShapesCoarse(const QVector<const Shape*>& shapesA, const QVector<const Shape*>& shapesB, CollisionInfo& collision);
/// \param shapeA a pointer to a shape
/// \param cubeCenter center of cube
/// \param cubeSide lenght of side of cube
/// \param collisions[out] average collision details
/// \return true if shapeA collides with axis aligned cube
bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
/// \param sphereA pointer to first shape
/// \param sphereB pointer to second shape
/// \param[out] collisions where to append collision details
@ -129,6 +136,20 @@ namespace ShapeCollider {
/// \return true if shapes collide
bool listList(const ListShape* listA, const ListShape* listB, CollisionList& collisions);
/// \param sphereA pointer to sphere
/// \param cubeCenter center of cube
/// \param cubeSide lenght of side of cube
/// \param[out] collisions where to append collision details
/// \return true if sphereA collides with axis aligned cube
bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
/// \param capsuleA pointer to capsule
/// \param cubeCenter center of cube
/// \param cubeSide lenght of side of cube
/// \param[out] collisions where to append collision details
/// \return true if capsuleA collides with axis aligned cube
bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
} // namespace ShapeCollider
#endif // hifi_ShapeCollider_h

View file

@ -656,27 +656,59 @@ void debug::checkDeadBeef(void* memoryVoid, int size) {
// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java)
glm::vec3 safeEulerAngles(const glm::quat& q) {
float sy = 2.0f * (q.y * q.w - q.x * q.z);
glm::vec3 eulers;
if (sy < 1.0f - EPSILON) {
if (sy > -1.0f + EPSILON) {
return glm::vec3(
eulers = glm::vec3(
atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)),
asinf(sy),
atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)));
} else {
// not a unique solution; x + z = atan2(-m21, m11)
return glm::vec3(
eulers = glm::vec3(
0.0f,
- PI_OVER_TWO,
atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
}
} else {
// not a unique solution; x - z = atan2(-m21, m11)
return glm::vec3(
eulers = glm::vec3(
0.0f,
PI_OVER_TWO,
-atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)));
}
// adjust so that z, rather than y, is in [-pi/2, pi/2]
if (eulers.z < -PI_OVER_TWO) {
if (eulers.x < 0.0f) {
eulers.x += PI;
} else {
eulers.x -= PI;
}
eulers.y = -eulers.y;
if (eulers.y < 0.0f) {
eulers.y += PI;
} else {
eulers.y -= PI;
}
eulers.z += PI;
} else if (eulers.z > PI_OVER_TWO) {
if (eulers.x < 0.0f) {
eulers.x += PI;
} else {
eulers.x -= PI;
}
eulers.y = -eulers.y;
if (eulers.y < 0.0f) {
eulers.y += PI;
} else {
eulers.y -= PI;
}
eulers.z -= PI;
}
return eulers;
}
// Helper function returns the positive angle (in radians) between two 3D vectors

View file

@ -18,10 +18,10 @@
#include <QReadWriteLock>
#include <AABox.h>
#include <OctreeElement.h>
#include <SharedUtil.h>
#include "AABox.h"
#include "ViewFrustum.h"
#include "VoxelConstants.h"

View file

@ -23,18 +23,18 @@
#include "ShapeColliderTests.h"
const glm::vec3 origin(0.f);
static const glm::vec3 xAxis(1.f, 0.f, 0.f);
static const glm::vec3 yAxis(0.f, 1.f, 0.f);
static const glm::vec3 zAxis(0.f, 0.f, 1.f);
const glm::vec3 origin(0.0f);
static const glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
static const glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
static const glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
void ShapeColliderTests::sphereMissesSphere() {
// non-overlapping spheres of unequal size
float radiusA = 7.f;
float radiusB = 3.f;
float radiusA = 7.0f;
float radiusB = 3.0f;
float alpha = 1.2f;
float beta = 1.3f;
glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.f, 2.f, 3.f));
glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
float offsetDistance = alpha * radiusA + beta * radiusB;
SphereShape sphereA(radiusA, origin);
@ -77,13 +77,13 @@ void ShapeColliderTests::sphereMissesSphere() {
void ShapeColliderTests::sphereTouchesSphere() {
// overlapping spheres of unequal size
float radiusA = 7.f;
float radiusB = 3.f;
float radiusA = 7.0f;
float radiusB = 3.0f;
float alpha = 0.2f;
float beta = 0.3f;
glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.f, 2.f, 3.f));
glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f));
float offsetDistance = alpha * radiusA + beta * radiusB;
float expectedPenetrationDistance = (1.f - alpha) * radiusA + (1.f - beta) * radiusB;
float expectedPenetrationDistance = (1.0f - alpha) * radiusA + (1.0f - beta) * radiusB;
glm::vec3 expectedPenetration = expectedPenetrationDistance * offsetDirection;
SphereShape sphereA(radiusA, origin);
@ -118,8 +118,7 @@ void ShapeColliderTests::sphereTouchesSphere() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad penetration: expected = " << expectedPenetration
<< " actual = " << collision->_penetration
<< std::endl;
<< " actual = " << collision->_penetration;
}
// contactPoint is on surface of sphereA
@ -129,8 +128,7 @@ void ShapeColliderTests::sphereTouchesSphere() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
<< " actual = " << collision->_contactPoint
<< std::endl;
<< " actual = " << collision->_contactPoint;
}
}
@ -150,8 +148,7 @@ void ShapeColliderTests::sphereTouchesSphere() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad penetration: expected = " << expectedPenetration
<< " actual = " << collision->_penetration
<< std::endl;
<< " actual = " << collision->_penetration;
}
// contactPoint is on surface of sphereA
@ -161,8 +158,7 @@ void ShapeColliderTests::sphereTouchesSphere() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
<< " actual = " << collision->_contactPoint
<< std::endl;
<< " actual = " << collision->_contactPoint;
}
}
}
@ -181,7 +177,7 @@ void ShapeColliderTests::sphereMissesCapsule() {
// give the capsule some arbirary transform
float angle = 37.8f;
glm::vec3 axis = glm::normalize( glm::vec3(-7.f, 2.8f, 9.3f) );
glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) );
glm::quat rotation = glm::angleAxis(angle, axis);
glm::vec3 translation(15.1f, -27.1f, -38.6f);
capsuleB.setRotation(rotation);
@ -190,7 +186,7 @@ void ShapeColliderTests::sphereMissesCapsule() {
CollisionList collisions(16);
// walk sphereA along the local yAxis next to, but not touching, capsuleB
glm::vec3 localStartPosition(radialOffset, axialOffset, 0.f);
glm::vec3 localStartPosition(radialOffset, axialOffset, 0.0f);
int numberOfSteps = 10;
float delta = 1.3f * (totalRadius + halfHeightB) / (numberOfSteps - 1);
for (int i = 0; i < numberOfSteps; ++i) {
@ -224,10 +220,10 @@ void ShapeColliderTests::sphereMissesCapsule() {
void ShapeColliderTests::sphereTouchesCapsule() {
// overlapping sphere and capsule
float radiusA = 2.f;
float radiusB = 1.f;
float radiusA = 2.0f;
float radiusB = 1.0f;
float totalRadius = radiusA + radiusB;
float halfHeightB = 2.f;
float halfHeightB = 2.0f;
float alpha = 0.5f;
float beta = 0.5f;
float radialOffset = alpha * radiusA + beta * radiusB;
@ -257,8 +253,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad penetration: expected = " << expectedPenetration
<< " actual = " << collision->_penetration
<< std::endl;
<< " actual = " << collision->_penetration;
}
// contactPoint is on surface of sphereA
@ -267,8 +262,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
<< " actual = " << collision->_contactPoint
<< std::endl;
<< " actual = " << collision->_contactPoint;
}
// capsuleB collides with sphereA
@ -288,8 +282,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad penetration: expected = " << expectedPenetration
<< " actual = " << collision->_penetration
<< std::endl;
<< " actual = " << collision->_penetration;
}
// contactPoint is on surface of capsuleB
@ -300,8 +293,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
<< " actual = " << collision->_contactPoint
<< std::endl;
<< " actual = " << collision->_contactPoint;
}
}
{ // sphereA hits end cap at axis
@ -319,13 +311,12 @@ void ShapeColliderTests::sphereTouchesCapsule() {
// penetration points from sphereA into capsuleB
CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
glm::vec3 expectedPenetration = - ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
glm::vec3 expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis;
float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad penetration: expected = " << expectedPenetration
<< " actual = " << collision->_penetration
<< std::endl;
<< " actual = " << collision->_penetration;
}
// contactPoint is on surface of sphereA
@ -334,8 +325,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
<< " actual = " << collision->_contactPoint
<< std::endl;
<< " actual = " << collision->_contactPoint;
}
// capsuleB collides with sphereA
@ -350,13 +340,12 @@ void ShapeColliderTests::sphereTouchesCapsule() {
// penetration points from sphereA into capsuleB
collision = collisions.getCollision(numCollisions - 1);
expectedPenetration = ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis;
inaccuracy = glm::length(collision->_penetration - expectedPenetration);
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad penetration: expected = " << expectedPenetration
<< " actual = " << collision->_penetration
<< std::endl;
<< " actual = " << collision->_penetration;
}
// contactPoint is on surface of capsuleB
@ -367,8 +356,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
<< " actual = " << collision->_contactPoint
<< std::endl;
<< " actual = " << collision->_contactPoint;
}
}
{ // sphereA hits start cap at axis
@ -386,13 +374,12 @@ void ShapeColliderTests::sphereTouchesCapsule() {
// penetration points from sphereA into capsuleB
CollisionInfo* collision = collisions.getCollision(numCollisions - 1);
glm::vec3 expectedPenetration = ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
glm::vec3 expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis;
float inaccuracy = glm::length(collision->_penetration - expectedPenetration);
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad penetration: expected = " << expectedPenetration
<< " actual = " << collision->_penetration
<< std::endl;
<< " actual = " << collision->_penetration;
}
// contactPoint is on surface of sphereA
@ -401,8 +388,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
<< " actual = " << collision->_contactPoint
<< std::endl;
<< " actual = " << collision->_contactPoint;
}
// capsuleB collides with sphereA
@ -417,13 +403,12 @@ void ShapeColliderTests::sphereTouchesCapsule() {
// penetration points from sphereA into capsuleB
collision = collisions.getCollision(numCollisions - 1);
expectedPenetration = - ((1.f - alpha) * radiusA + (1.f - beta) * radiusB) * yAxis;
expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis;
inaccuracy = glm::length(collision->_penetration - expectedPenetration);
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad penetration: expected = " << expectedPenetration
<< " actual = " << collision->_penetration
<< std::endl;
<< " actual = " << collision->_penetration;
}
// contactPoint is on surface of capsuleB
@ -434,8 +419,7 @@ void ShapeColliderTests::sphereTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
<< " actual = " << collision->_contactPoint
<< std::endl;
<< " actual = " << collision->_contactPoint;
}
}
if (collisions.size() != numCollisions) {
@ -447,10 +431,10 @@ void ShapeColliderTests::sphereTouchesCapsule() {
void ShapeColliderTests::capsuleMissesCapsule() {
// non-overlapping capsules
float radiusA = 2.f;
float halfHeightA = 3.f;
float radiusB = 3.f;
float halfHeightB = 4.f;
float radiusA = 2.0f;
float halfHeightA = 3.0f;
float radiusB = 3.0f;
float halfHeightB = 4.0f;
float totalRadius = radiusA + radiusB;
float totalHalfLength = totalRadius + halfHeightA + halfHeightB;
@ -516,10 +500,10 @@ void ShapeColliderTests::capsuleMissesCapsule() {
void ShapeColliderTests::capsuleTouchesCapsule() {
// overlapping capsules
float radiusA = 2.f;
float halfHeightA = 3.f;
float radiusB = 3.f;
float halfHeightB = 4.f;
float radiusA = 2.0f;
float halfHeightA = 3.0f;
float radiusB = 3.0f;
float halfHeightB = 4.0f;
float totalRadius = radiusA + radiusB;
float totalHalfLength = totalRadius + halfHeightA + halfHeightB;
@ -617,8 +601,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad penetration: expected = " << expectedPenetration
<< " actual = " << collision->_penetration
<< std::endl;
<< " actual = " << collision->_penetration;
}
glm::vec3 expectedContactPoint = capsuleA.getPosition() + radiusA * xAxis;
@ -626,8 +609,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() {
if (fabs(inaccuracy) > EPSILON) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: bad contactPoint: expected = " << expectedContactPoint
<< " actual = " << collision->_contactPoint
<< std::endl;
<< " actual = " << collision->_contactPoint;
}
// capsuleB vs capsuleA
@ -699,6 +681,116 @@ void ShapeColliderTests::capsuleTouchesCapsule() {
}
}
void ShapeColliderTests::sphereTouchesAACube() {
CollisionList collisions(16);
glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f);
float cubeSide = 2.0f;
float sphereRadius = 1.0f;
glm::vec3 sphereCenter(0.0f);
SphereShape sphere(sphereRadius, sphereCenter);
float sphereOffset = (0.5f * cubeSide + sphereRadius - 0.25f);
// top
sphereCenter = cubeCenter + sphereOffset * yAxis;
sphere.setPosition(sphereCenter);
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
}
// bottom
sphereCenter = cubeCenter - sphereOffset * yAxis;
sphere.setPosition(sphereCenter);
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
}
// left
sphereCenter = cubeCenter + sphereOffset * xAxis;
sphere.setPosition(sphereCenter);
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
}
// right
sphereCenter = cubeCenter - sphereOffset * xAxis;
sphere.setPosition(sphereCenter);
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
}
// forward
sphereCenter = cubeCenter + sphereOffset * zAxis;
sphere.setPosition(sphereCenter);
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
}
// back
sphereCenter = cubeCenter - sphereOffset * zAxis;
sphere.setPosition(sphereCenter);
if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube" << std::endl;
}
}
void ShapeColliderTests::sphereMissesAACube() {
CollisionList collisions(16);
glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f);
float cubeSide = 2.0f;
float sphereRadius = 1.0f;
glm::vec3 sphereCenter(0.0f);
SphereShape sphere(sphereRadius, sphereCenter);
float sphereOffset = (0.5f * cubeSide + sphereRadius + 0.25f);
// top
sphereCenter = cubeCenter + sphereOffset * yAxis;
sphere.setPosition(sphereCenter);
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
}
// bottom
sphereCenter = cubeCenter - sphereOffset * yAxis;
sphere.setPosition(sphereCenter);
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
}
// left
sphereCenter = cubeCenter + sphereOffset * xAxis;
sphere.setPosition(sphereCenter);
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
}
// right
sphereCenter = cubeCenter - sphereOffset * xAxis;
sphere.setPosition(sphereCenter);
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
}
// forward
sphereCenter = cubeCenter + sphereOffset * zAxis;
sphere.setPosition(sphereCenter);
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
}
// back
sphereCenter = cubeCenter - sphereOffset * zAxis;
sphere.setPosition(sphereCenter);
if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl;
}
}
void ShapeColliderTests::runAllTests() {
sphereMissesSphere();
@ -709,4 +801,7 @@ void ShapeColliderTests::runAllTests() {
capsuleMissesCapsule();
capsuleTouchesCapsule();
sphereTouchesAACube();
sphereMissesAACube();
}

View file

@ -23,6 +23,9 @@ namespace ShapeColliderTests {
void capsuleMissesCapsule();
void capsuleTouchesCapsule();
void sphereTouchesAACube();
void sphereMissesAACube();
void runAllTests();
}