This commit is contained in:
Philip Rosedale 2014-05-07 17:00:50 -07:00
commit a86cd9afa1
25 changed files with 265 additions and 92 deletions

View file

@ -54,7 +54,7 @@
const short JITTER_BUFFER_MSECS = 12;
const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0);
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00305f;
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
@ -348,6 +348,16 @@ void AudioMixer::readPendingDatagrams() {
|| mixerPacketType == PacketTypeSilentAudioFrame) {
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
} else if (mixerPacketType == PacketTypeMuteEnvironment) {
QByteArray packet = receivedPacket;
populatePacketHeader(packet, PacketTypeMuteEnvironment);
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) {
nodeList->writeDatagram(packet, packet.size(), node);
}
}
} else {
// let processNodeData handle it.
nodeList->processNodeData(senderSockAddr, receivedPacket);

View file

@ -370,7 +370,7 @@ function moveOverlays() {
toolsX = windowDimensions.x - 8 - toolWidth;
toolsY = (windowDimensions.y - toolsHeight) / 2;
Overlays.addOverlay(firstModel, {
Overlays.editOverlay(firstModel, {
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight,
});
}

View file

@ -131,6 +131,20 @@ void DatagramProcessor::processDatagrams() {
break;
}
case PacketTypeMuteEnvironment: {
glm::vec3 position;
float radius;
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3));
memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float));
if (glm::distance(Application::getInstance()->getAvatar()->getPosition(), position) < radius
&& !Application::getInstance()->getAudio()->getMuted()) {
Application::getInstance()->getAudio()->toggleMute();
}
break;
}
default:
nodeList->processNodeData(senderSockAddr, incomingPacket);
break;

View file

@ -68,6 +68,7 @@ const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f;
const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f;
const int ONE_SECOND_OF_FRAMES = 60;
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
const float MUTE_RADIUS = 50;
Menu::Menu() :
_actionHash(),
@ -397,6 +398,11 @@ Menu::Menu() :
false,
appInstance->getAudio(),
SLOT(toggleMute()));
addActionToQMenuAndActionHash(audioDebugMenu,
MenuOption::MuteEnvironment,
0,
this,
SLOT(muteEnvironment()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioToneInjection,
0,
false,
@ -1000,6 +1006,30 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson
disconnect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision);
}
void Menu::muteEnvironment() {
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
int packetSize = headerSize + sizeof(glm::vec3) + sizeof(float);
glm::vec3 position = Application::getInstance()->getAvatar()->getPosition();
char* packet = (char*)malloc(packetSize);
populatePacketHeader(packet, PacketTypeMuteEnvironment);
memcpy(packet + headerSize, &position, sizeof(glm::vec3));
memcpy(packet + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float));
QByteArray mutePacket(packet, packetSize);
// grab our audio mixer from the NodeList, if it exists
SharedNodePointer audioMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AudioMixer);
if (audioMixer) {
// send off this mute packet
NodeList::getInstance()->writeDatagram(mutePacket, audioMixer);
}
free(packet);
}
void Menu::goToLocation() {
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
glm::vec3 avatarPos = myAvatar->getPosition();

View file

@ -191,6 +191,7 @@ private slots:
void audioMuteToggled();
void namedLocationCreated(LocationManager::NamedLocationCreateResponse response);
void multipleDestinationsDecision(const QJsonObject& userData, const QJsonObject& placeData);
void muteEnvironment();
private:
static Menu* _instance;
@ -339,6 +340,7 @@ namespace MenuOption {
const QString Models = "Models";
const QString MoveWithLean = "Move with Lean";
const QString MuteAudio = "Mute Microphone";
const QString MuteEnvironment = "Mute Environment";
const QString NameLocation = "Name this location";
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
const QString OctreeStats = "Voxel and Particle Statistics";

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QCheckBox>
#include <QComboBox>
#include <QDebug>
#include <QDialogButtonBox>
@ -42,6 +43,9 @@ static const QString TEXDIR_FIELD = "texdir";
static const QString LOD_FIELD = "lod";
static const QString JOINT_INDEX_FIELD = "jointIndex";
static const QString SCALE_FIELD = "scale";
static const QString TRANSLATION_X_FIELD = "tx";
static const QString TRANSLATION_Y_FIELD = "ty";
static const QString TRANSLATION_Z_FIELD = "tz";
static const QString JOINT_FIELD = "joint";
static const QString FREE_JOINT_FIELD = "freeJoint";
@ -519,6 +523,14 @@ bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const
return true;
}
static QDoubleSpinBox* createTranslationBox() {
QDoubleSpinBox* box = new QDoubleSpinBox();
const double MAX_TRANSLATION = 1000000.0;
box->setMinimum(-MAX_TRANSLATION);
box->setMaximum(MAX_TRANSLATION);
return box;
}
ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping,
const QString& basePath, const FBXGeometry& geometry) :
_modelType(modelType),
@ -540,7 +552,18 @@ ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariant
_scale->setMaximum(FLT_MAX);
_scale->setSingleStep(0.01);
if (_modelType != ATTACHMENT_MODEL) {
if (_modelType == ATTACHMENT_MODEL) {
QHBoxLayout* translation = new QHBoxLayout();
form->addRow("Translation:", translation);
translation->addWidget(_translationX = createTranslationBox());
translation->addWidget(_translationY = createTranslationBox());
translation->addWidget(_translationZ = createTranslationBox());
form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox());
form->addRow("Pivot Joint:", _pivotJoint = createJointBox());
connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint()));
_pivotAboutCenter->setChecked(true);
} else {
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
form->addRow("Neck Joint:", _neckJoint = createJointBox());
@ -584,7 +607,19 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
if (_modelType != ATTACHMENT_MODEL) {
if (_modelType == ATTACHMENT_MODEL) {
glm::vec3 pivot;
if (_pivotAboutCenter->isChecked()) {
pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
} else if (_pivotJoint->currentIndex() != 0) {
pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
}
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value());
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value());
mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value());
} else {
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
@ -617,7 +652,14 @@ void ModelPropertiesDialog::reset() {
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
if (_modelType != ATTACHMENT_MODEL) {
if (_modelType == ATTACHMENT_MODEL) {
_translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble());
_translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble());
_translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble());
_pivotAboutCenter->setChecked(true);
_pivotJoint->setCurrentIndex(0);
} else {
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
@ -654,6 +696,10 @@ void ModelPropertiesDialog::chooseTextureDirectory() {
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
}
void ModelPropertiesDialog::updatePivotJoint() {
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
}
void ModelPropertiesDialog::createNewFreeJoint(const QString& joint) {
QWidget* freeJoint = new QWidget();
QHBoxLayout* freeJointLayout = new QHBoxLayout();

View file

@ -19,6 +19,7 @@
#include "ui/ModelsBrowser.h"
class QCheckBox;
class QComboBox;
class QDoubleSpinBox;
class QFileInfo;
@ -83,6 +84,7 @@ public:
private slots:
void reset();
void chooseTextureDirectory();
void updatePivotJoint();
void createNewFreeJoint(const QString& joint = QString());
private:
@ -96,6 +98,11 @@ private:
QLineEdit* _name;
QPushButton* _textureDirectory;
QDoubleSpinBox* _scale;
QDoubleSpinBox* _translationX;
QDoubleSpinBox* _translationY;
QDoubleSpinBox* _translationZ;
QCheckBox* _pivotAboutCenter;
QComboBox* _pivotJoint;
QComboBox* _leftEyeJoint;
QComboBox* _rightEyeJoint;
QComboBox* _neckJoint;

View file

@ -634,8 +634,8 @@ bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float parti
penetration)) {
CollisionInfo* collision = collisions.getNewCollision();
if (collision) {
collision->_type = PADDLE_HAND_COLLISION;
collision->_flags = jointIndex;
collision->_type = COLLISION_TYPE_PADDLE_HAND;
collision->_intData = jointIndex;
collision->_penetration = penetration;
collision->_addedVelocity = palm->getVelocity();
collided = true;
@ -844,11 +844,11 @@ float Avatar::getHeadHeight() const {
}
bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
if (!collision._data || collision._type != MODEL_COLLISION) {
if (!collision._data || collision._type != COLLISION_TYPE_MODEL) {
return false;
}
Model* model = static_cast<Model*>(collision._data);
int jointIndex = collision._flags;
int jointIndex = collision._intData;
if (model == &(_skeletonModel) && jointIndex != -1) {
// collision response of skeleton is temporarily disabled
@ -856,7 +856,7 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const {
//return _skeletonModel.collisionHitsMoveableJoint(collision);
}
if (model == &(getHead()->getFaceModel())) {
// ATM we always handle MODEL_COLLISIONS against the face.
// ATM we always handle COLLISION_TYPE_MODEL against the face.
return true;
}
return false;

View file

@ -21,6 +21,7 @@
#include <QtCore/QTimer>
#include <AccountManager.h>
#include <GeometryUtil.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
@ -63,6 +64,7 @@ MyAvatar::MyAvatar() :
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
_wasPushing(false),
_isPushing(false),
_wasStuck(false),
_thrust(0.0f),
_motorVelocity(0.0f),
_motorTimescale(DEFAULT_MOTOR_TIMESCALE),
@ -212,6 +214,8 @@ void MyAvatar::simulate(float deltaTime) {
}
if (_collisionGroups & COLLISION_GROUP_VOXELS) {
updateCollisionWithVoxels(deltaTime, radius);
} else {
_wasStuck = false;
}
if (_collisionGroups & COLLISION_GROUP_AVATARS) {
updateCollisionWithAvatars(deltaTime);
@ -958,65 +962,48 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
static CollisionList myCollisions(64);
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
const float MIN_STUCK_SPEED = 100.0f;
float speed = glm::length(_velocity);
if (speed > MIN_STUCK_SPEED) {
// don't even bother to try to collide against voxles when moving very fast
return;
}
myCollisions.clear();
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions)) {
const float VOXEL_ELASTICITY = 0.0f;
const float VOXEL_DAMPING = 0.0f;
float capsuleRadius = boundingShape.getRadius();
if (glm::length2(_gravity) > EPSILON) {
if (myCollisions.size() == 1) {
// trivial case
CollisionInfo* collision = myCollisions[0];
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
_lastFloorContactPoint = collision->_contactPoint - collision->_penetration;
} else {
// This is special collision handling for when walking on a voxel field which
// prevents snagging at corners and seams.
// sift through the collisions looking for one against the "floor"
int floorIndex = 0;
float distanceToFloor = 0.0f;
float penetrationWithFloor = 0.0f;
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
float distance = glm::dot(_gravity, collision->_contactPoint - _position);
if (distance > distanceToFloor) {
distanceToFloor = distance;
penetrationWithFloor = glm::dot(_gravity, collision->_penetration);
floorIndex = i;
}
}
// step through the collisions again and apply each that is not redundant
glm::vec3 oldPosition = _position;
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
if (i == floorIndex) {
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
_lastFloorContactPoint = collision->_contactPoint - collision->_penetration;
} else {
float distance = glm::dot(_gravity, collision->_contactPoint - oldPosition);
float penetration = glm::dot(_gravity, collision->_penetration);
if (fabsf(distance - distanceToFloor) > penetrationWithFloor || penetration > penetrationWithFloor) {
// resolution of the deepest penetration would not resolve this one
// so we apply the collision
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
glm::vec3 totalPenetration(0.0f);
bool isStuck = false;
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
float depth = glm::length(collision->_penetration);
if (depth > capsuleRadius) {
isStuck = true;
if (_wasStuck) {
glm::vec3 cubeCenter = collision->_vecData;
float cubeSide = collision->_floatData;
float distance = glm::dot(boundingShape.getPosition() - cubeCenter, _worldUpDirection);
if (distance < 0.0f) {
distance = fabsf(distance) + 0.5f * cubeSide;
}
distance += capsuleRadius + boundingShape.getHalfHeight();
totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection);
continue;
}
}
} else {
// no gravity -- apply all collisions
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
applyHardCollision(collision->_penetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
}
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
_wasStuck = isStuck;
const float VOXEL_COLLISION_FREQUENCY = 0.5f;
updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
}
} else {
_wasStuck = false;
}
}
void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) {

View file

@ -125,6 +125,7 @@ private:
bool _wasPushing;
bool _isPushing;
bool _wasStuck;
glm::vec3 _thrust; // final acceleration from outside sources for the current frame
glm::vec3 _motorVelocity; // intended velocity of avatar motion

View file

@ -789,9 +789,9 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi
}
if (ShapeCollider::collideShapes(&sphere, _jointShapes[i], collisions)) {
CollisionInfo* collision = collisions.getLastCollision();
collision->_type = MODEL_COLLISION;
collision->_type = COLLISION_TYPE_MODEL;
collision->_data = (void*)(this);
collision->_flags = i;
collision->_intData = i;
collided = true;
}
outerContinue: ;
@ -805,9 +805,9 @@ bool Model::findPlaneCollisions(const glm::vec4& plane, CollisionList& collision
for (int i = 0; i < _jointShapes.size(); i++) {
if (ShapeCollider::collideShapes(&planeShape, _jointShapes[i], collisions)) {
CollisionInfo* collision = collisions.getLastCollision();
collision->_type = MODEL_COLLISION;
collision->_type = COLLISION_TYPE_MODEL;
collision->_data = (void*)(this);
collision->_flags = i;
collision->_intData = i;
collided = true;
}
}
@ -1256,15 +1256,15 @@ void Model::renderBoundingCollisionShapes(float alpha) {
}
bool Model::collisionHitsMoveableJoint(CollisionInfo& collision) const {
if (collision._type == MODEL_COLLISION) {
if (collision._type == COLLISION_TYPE_MODEL) {
// the joint is pokable by a collision if it exists and is free to move
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._flags];
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._intData];
if (joint.parentIndex == -1 || _jointStates.isEmpty()) {
return false;
}
// an empty freeLineage means the joint can't move
const FBXGeometry& geometry = _geometry->getFBXGeometry();
int jointIndex = collision._flags;
int jointIndex = collision._intData;
const QVector<int>& freeLineage = geometry.joints.at(jointIndex).freeLineage;
return !freeLineage.isEmpty();
}
@ -1272,12 +1272,12 @@ bool Model::collisionHitsMoveableJoint(CollisionInfo& collision) const {
}
void Model::applyCollision(CollisionInfo& collision) {
if (collision._type != MODEL_COLLISION) {
if (collision._type != COLLISION_TYPE_MODEL) {
return;
}
glm::vec3 jointPosition(0.f);
int jointIndex = collision._flags;
int jointIndex = collision._intData;
if (getJointPosition(jointIndex, jointPosition)) {
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
if (joint.parentIndex != -1) {

View file

@ -21,6 +21,12 @@
#include "AudioInjector.h"
AudioInjector::AudioInjector(QObject* parent) :
QObject(parent)
{
}
AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) :
_sound(sound),
_options(injectorOptions)
@ -80,7 +86,7 @@ void AudioInjector::injectAudio() {
int numPreAudioDataBytes = injectAudioPacket.size();
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
while (currentSendPosition < soundByteArray.size()) {
while (currentSendPosition < soundByteArray.size() && !_shouldStop) {
int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
soundByteArray.size() - currentSendPosition);

View file

@ -24,14 +24,19 @@
class AudioInjector : public QObject {
Q_OBJECT
public:
AudioInjector(QObject* parent);
AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions);
public slots:
void injectAudio();
void stop() { _shouldStop = true; }
signals:
void finished();
private:
Sound* _sound;
AudioInjectorOptions _options;
public slots:
void injectAudio();
signals:
void finished();
bool _shouldStop;
};
Q_DECLARE_METATYPE(AudioInjector*)
#endif // hifi_AudioInjector_h

View file

@ -11,7 +11,7 @@
#include "AudioScriptingInterface.h"
void AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) {
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) {
AudioInjector* injector = new AudioInjector(sound, *injectorOptions);
@ -28,6 +28,8 @@ void AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions
connect(injectorThread, SIGNAL(finished()), injectorThread, SLOT(deleteLater()));
injectorThread->start();
return injector;
}
void AudioScriptingInterface::startDrumSound(float volume, float frequency, float duration, float decay,

View file

@ -20,7 +20,7 @@ const AudioInjectorOptions DEFAULT_INJECTOR_OPTIONS;
class AudioScriptingInterface : public QObject {
Q_OBJECT
public slots:
static void playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL);
static AudioInjector* playSound(Sound* sound, const AudioInjectorOptions* injectorOptions = NULL);
static void startDrumSound(float volume, float frequency, float duration, float decay,
const AudioInjectorOptions* injectorOptions = NULL);

View file

@ -89,7 +89,7 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() {
const int TRAILING_AVERAGE_FRAMES = 100;
const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;
const float LOUDNESS_EPSILON = 0.01f;
const float LOUDNESS_EPSILON = 0.000001f;
if (nextLoudness >= _nextOutputTrailingLoudness) {
_nextOutputTrailingLoudness = nextLoudness;

View file

@ -23,6 +23,7 @@
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <StreamUtils.h>
#include <UUID.h>
#include <VoxelConstants.h>

View file

@ -45,7 +45,6 @@ typedef unsigned long long quint64;
#include <CollisionInfo.h>
#include <RegisteredMetaTypes.h>
#include <StreamUtils.h>
#include <Node.h>

View file

@ -826,7 +826,7 @@ ExtractedMesh extractMesh(const FBXNode& object) {
while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0);
QPair<int, int> materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0,
(polygonIndex < textures.size()) ? textures.at(polygonIndex) : -1);
(polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0);
int& partIndex = materialTextureParts[materialTexture];
if (partIndex == 0) {
data.extracted.partMaterialTextures.append(materialTexture);
@ -972,6 +972,18 @@ FBXTexture getTexture(const QString& textureID, const QHash<QString, QByteArray>
return texture;
}
bool checkMaterialsHaveTextures(const QHash<QString, Material>& materials,
const QHash<QString, QByteArray>& textureFilenames, const QMultiHash<QString, QString>& childMap) {
foreach (const QString& materialID, materials.keys()) {
foreach (const QString& childID, childMap.values(materialID)) {
if (textureFilenames.contains(childID)) {
return true;
}
}
}
return false;
}
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
QHash<QString, ExtractedMesh> meshes;
QVector<ExtractedBlendshape> blendshapes;
@ -1515,6 +1527,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
geometry.bindExtents.reset();
geometry.meshExtents.reset();
// see if any materials have texture children
bool materialsHaveTextures = checkMaterialsHaveTextures(materials, textureFilenames, childMap);
for (QHash<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
ExtractedMesh& extracted = it.value();
@ -1587,7 +1602,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
} else if (textureFilenames.contains(childID)) {
FBXTexture texture = getTexture(childID, textureFilenames, textureContent);
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
if (extracted.partMaterialTextures.at(j).second == textureIndex) {
int partTexture = extracted.partMaterialTextures.at(j).second;
if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) {
extracted.mesh.parts[j].diffuseTexture = texture;
}
}

View file

@ -39,7 +39,7 @@ enum PacketType {
PacketTypeRequestAssignment,
PacketTypeCreateAssignment,
PacketTypeDomainOAuthRequest,
PacketTypeDataServerGet, // reusable
PacketTypeMuteEnvironment,
PacketTypeDataServerSend, // reusable
PacketTypeDataServerConfirm,
PacketTypeVoxelQuery,

View file

@ -224,7 +224,7 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
// (doing this prevents some "collision snagging" when particle penetrates the object)
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against them.
if (collision->_type == PADDLE_HAND_COLLISION) {
if (collision->_type == COLLISION_TYPE_PADDLE_HAND) {
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle.
// TODO: make this less hacky when we have more per-collision details
float elasticity = ELASTICITY;

View file

@ -18,6 +18,7 @@
#include <QtNetwork/QNetworkReply>
#include <QScriptEngine>
#include <AudioInjector.h>
#include <AudioRingBuffer.h>
#include <AvatarData.h>
#include <CollisionInfo.h>
@ -52,6 +53,14 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
return QScriptValue();
}
QScriptValue injectorToScriptValue(QScriptEngine *engine, AudioInjector* const &in) {
return engine->newQObject(in);
}
void injectorFromScriptValue(const QScriptValue &object, AudioInjector* &out) {
out = qobject_cast<AudioInjector*>(object.toQObject());
}
ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString,
AbstractControllerScriptingInterface* controllerScriptingInterface) :
@ -226,6 +235,8 @@ void ScriptEngine::init() {
QScriptValue localVoxelsValue = _engine.scriptValueFromQMetaObject<LocalVoxels>();
_engine.globalObject().setProperty("LocalVoxels", localVoxelsValue);
qScriptRegisterMetaType(&_engine, injectorToScriptValue, injectorFromScriptValue);
registerGlobalObject("Script", this);
registerGlobalObject("Audio", &_audioScriptingInterface);

View file

@ -38,19 +38,24 @@ CollisionInfo* CollisionList::getLastCollision() {
}
void CollisionList::clear() {
// we rely on the external context to properly set or clear the data members of a collision
// whenever it is used.
/*
for (int i = 0; i < _size; ++i) {
// we only clear the important stuff
CollisionInfo& collision = _collisions[i];
collision._type = BASE_COLLISION;
collision._data = NULL; // CollisionInfo does not own whatever this points to.
collision._flags = 0;
// we rely on the consumer to properly overwrite these fields when the collision is "created"
collision._type = COLLISION_TYPE_UNKNOWN;
//collision._data = NULL;
//collision._intData = 0;
//collision._floatDAta = 0.0f;
//collision._vecData = glm::vec3(0.0f);
//collision._damping;
//collision._elasticity;
//collision._contactPoint;
//collision._penetration;
//collision._addedVelocity;
}
*/
_size = 0;
}

View file

@ -18,9 +18,14 @@
#include <QVector>
enum CollisionType {
BASE_COLLISION = 0,
PADDLE_HAND_COLLISION,
MODEL_COLLISION,
COLLISION_TYPE_UNKNOWN = 0,
COLLISION_TYPE_PADDLE_HAND,
COLLISION_TYPE_MODEL,
// _data = pointer to Model that owns joint
// _intData = joint index
COLLISION_TYPE_AACUBE,
// _floatData = cube side
// _vecData = cube center
};
const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0;
@ -39,7 +44,7 @@ public:
CollisionInfo()
: _type(0),
_data(NULL),
_flags(0),
_intData(0),
_damping(0.f),
_elasticity(1.f),
_contactPoint(0.f),
@ -50,7 +55,7 @@ public:
CollisionInfo(qint32 type)
: _type(type),
_data(NULL),
_flags(0),
_intData(0),
_damping(0.f),
_elasticity(1.f),
_contactPoint(0.f),
@ -60,9 +65,13 @@ public:
~CollisionInfo() {}
qint32 _type; // type of Collision (will determine what is supposed to be in _data and _flags)
void* _data; // pointer to user supplied data
quint32 _flags; // 32 bits for whatever
int _type; // type of Collision
// the value of the *Data fields depend on the type
void* _data;
int _intData;
float _floatData;
glm::vec3 _vecData;
float _damping; // range [0,1] of friction coeficient
float _elasticity; // range [0,1] of energy conservation

View file

@ -132,6 +132,7 @@ bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, Collis
// penetration points from A into B
CollisionInfo* collision = collisions.getNewCollision();
if (collision) {
collision->_type = COLLISION_TYPE_UNKNOWN;
collision->_penetration = BA * (totalRadius - distance);
// contactPoint is on surface of A
collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * BA;
@ -179,6 +180,7 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
collision->_penetration = (totalRadius - radialDistance) * radialAxis; // points from A into B
// contactPoint is on surface of sphereA
collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * radialAxis;
collision->_type = COLLISION_TYPE_UNKNOWN;
} else {
// A is on B's axis, so the penetration is undefined...
if (absAxialDistance > capsuleB->getHalfHeight()) {
@ -200,6 +202,7 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
collision->_penetration = (sign * (totalRadius + capsuleB->getHalfHeight() - absAxialDistance)) * capsuleAxis;
// contactPoint is on surface of sphereA
collision->_contactPoint = sphereA->getPosition() + (sign * sphereA->getRadius()) * capsuleAxis;
collision->_type = COLLISION_TYPE_UNKNOWN;
}
return true;
}
@ -215,6 +218,7 @@ bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, Collision
}
collision->_penetration = penetration;
collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * glm::normalize(penetration);
collision->_type = COLLISION_TYPE_UNKNOWN;
return true;
}
return false;
@ -264,6 +268,7 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col
collision->_penetration = (radialDistance - totalRadius) * radialAxis; // points from A into B
// contactPoint is on surface of capsuleA
collision->_contactPoint = closestApproach - capsuleA->getRadius() * radialAxis;
collision->_type = COLLISION_TYPE_UNKNOWN;
} else {
// A is on B's axis, so the penetration is undefined...
if (absAxialDistance > capsuleA->getHalfHeight()) {
@ -284,6 +289,7 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col
collision->_penetration = (sign * (totalRadius + capsuleA->getHalfHeight() - absAxialDistance)) * capsuleAxis;
// contactPoint is on surface of sphereA
collision->_contactPoint = closestApproach + (sign * capsuleA->getRadius()) * capsuleAxis;
collision->_type = COLLISION_TYPE_UNKNOWN;
}
}
return true;
@ -355,6 +361,7 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB,
collision->_penetration = BA * (totalRadius - distance);
// contactPoint is on surface of A
collision->_contactPoint = centerA + distanceA * axisA + capsuleA->getRadius() * BA;
collision->_type = COLLISION_TYPE_UNKNOWN;
return true;
}
} else {
@ -420,6 +427,7 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB,
// average the internal pair, and then do the math from centerB
collision->_contactPoint = centerB + (0.5f * (points[1] + points[2])) * axisB
+ (capsuleA->getRadius() - distance) * BA;
collision->_type = COLLISION_TYPE_UNKNOWN;
return true;
}
}
@ -439,6 +447,7 @@ bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, Collis
collision->_penetration = penetration;
glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end;
collision->_contactPoint = deepestEnd + capsuleA->getRadius() * glm::normalize(penetration);
collision->_type = COLLISION_TYPE_UNKNOWN;
return true;
}
return false;
@ -454,6 +463,7 @@ bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, Collision
collision->_penetration = -penetration;
collision->_contactPoint = sphereB->getPosition() +
(sphereB->getRadius() / glm::length(penetration) - 1.0f) * penetration;
collision->_type = COLLISION_TYPE_UNKNOWN;
return true;
}
return false;
@ -472,6 +482,7 @@ bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, Collis
collision->_penetration = -penetration;
glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end;
collision->_contactPoint = deepestEnd + (capsuleB->getRadius() / glm::length(penetration) - 1.0f) * penetration;
collision->_type = COLLISION_TYPE_UNKNOWN;
return true;
}
return false;
@ -653,12 +664,19 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::
glm::vec3 direction;
BA /= maxBA;
glm::modf(BA, direction);
direction = glm::normalize(direction);
float lengthDirection = glm::length(direction);
direction /= lengthDirection;
// compute collision details
collision->_penetration = (halfCubeSide + sphereRadius - distance * glm::dot(BA, direction)) * direction;
collision->_type = COLLISION_TYPE_AACUBE;
collision->_floatData = cubeSide;
collision->_vecData = cubeCenter;
collision->_penetration = (halfCubeSide * lengthDirection + sphereRadius - maxBA * glm::dot(BA, direction)) * direction;
collision->_contactPoint = sphereCenter + sphereRadius * direction;
}
collision->_type = COLLISION_TYPE_AACUBE;
collision->_floatData = cubeSide;
collision->_vecData = cubeCenter;
return true;
} else if (sphereRadius + halfCubeSide > distance) {
// NOTE: for cocentric approximation we collide sphere and cube as two spheres which means
@ -669,6 +687,10 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::
collision->_penetration = (sphereRadius + halfCubeSide) * glm::vec3(0.0f, -1.0f, 0.0f);
// contactPoint is on surface of A
collision->_contactPoint = sphereCenter + collision->_penetration;
collision->_type = COLLISION_TYPE_AACUBE;
collision->_floatData = cubeSide;
collision->_vecData = cubeCenter;
return true;
}
}