mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 12:35:31 +02:00
Merge branch 'master' into ajt/new-anim-system
This commit is contained in:
commit
d151474446
33 changed files with 341 additions and 169 deletions
assignment-client/src/avatars
cmake/externals/sixense
examples
interface/src
libraries
animation/src
avatars/src
display-plugins/src/display-plugins
entities/src
input-plugins/src/input-plugins
networking/src/udt
render-utils/src
script-engine/src
shared/src
tests
|
@ -295,7 +295,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
avatarPacketList.startSegment();
|
||||
|
||||
numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122());
|
||||
numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray());
|
||||
numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray(false));
|
||||
|
||||
avatarPacketList.endSegment();
|
||||
|
||||
|
|
|
@ -77,10 +77,7 @@ void ScriptableAvatar::update(float deltatime) {
|
|||
int mapping = animationJoints.indexOf(modelJoints[i]);
|
||||
if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) {
|
||||
JointData& data = _jointData[i];
|
||||
data.valid = true;
|
||||
data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
||||
} else {
|
||||
_jointData[i].valid = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
4
cmake/externals/sixense/CMakeLists.txt
vendored
4
cmake/externals/sixense/CMakeLists.txt
vendored
|
@ -47,13 +47,13 @@ if (WIN32)
|
|||
elseif(APPLE)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx32/libopenvr_api.dylib CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32)
|
||||
|
||||
elseif(NOT ANDROID)
|
||||
|
||||
# FIXME need to account for different architectures
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux32/libopenvr_api.so CACHE TYPE INTERNAL)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
|
||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32)
|
||||
|
||||
endif()
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
elRefresh = document.getElementById("refresh");
|
||||
elDelete = document.getElementById("delete");
|
||||
elTeleport = document.getElementById("teleport");
|
||||
elRadius = document.getElementById("radius");
|
||||
elNoEntitiesMessage = document.getElementById("no-entities");
|
||||
elNoEntitiesRadius = document.getElementById("no-entities-radius");
|
||||
|
||||
document.getElementById("entity-name").onclick = function() {
|
||||
setSortColumn('name');
|
||||
|
@ -186,6 +188,13 @@
|
|||
}
|
||||
}, false);
|
||||
|
||||
elRadius.onchange = function () {
|
||||
elRadius.value = Math.max(elRadius.value, 0);
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value }));
|
||||
refreshEntities();
|
||||
elNoEntitiesRadius.firstChild.nodeValue = elRadius.value;
|
||||
}
|
||||
|
||||
if (window.EventBridge !== undefined) {
|
||||
EventBridge.scriptEventReceived.connect(function(data) {
|
||||
data = JSON.parse(data);
|
||||
|
@ -218,14 +227,15 @@
|
|||
</head>
|
||||
<body onload='loaded();'>
|
||||
<div id="entity-list-header">
|
||||
<input type="button" id="refresh" value="Refresh"></button>
|
||||
<input type="button" id="teleport" value="Teleport"></button>
|
||||
<input type="button" id="delete" style="background-color: rgb(244, 64, 64); float: right" value="Delete"></button>
|
||||
<input type="button" id="refresh" value="Refresh" />
|
||||
<input type="button" id="teleport" value="Teleport" />
|
||||
<input type="button" id="delete" style="background-color: rgb(244, 64, 64); float: right" value="Delete" />
|
||||
</div>
|
||||
|
||||
<div id="entity-list">
|
||||
<div id="search-area">
|
||||
<input type="text" class="search" id="filter" placeholder="Filter" />
|
||||
<span id="radius-and-unit"><input type="number" id="radius" value="100" /> m</span>
|
||||
</div>
|
||||
<table id="entity-table">
|
||||
<thead>
|
||||
|
@ -246,7 +256,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<div id="no-entities">
|
||||
No entities found within 50 meter radius. Try moving to a different location and refreshing.
|
||||
No entities found within a <span id="no-entities-radius">100</span> meter radius. Try moving to a different location and refreshing.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -102,13 +102,23 @@ input[type=button] {
|
|||
}
|
||||
|
||||
#search-area {
|
||||
width: 100%;
|
||||
padding: 0.5em;
|
||||
box-sizing: border-box;
|
||||
padding-right: 6em;
|
||||
}
|
||||
|
||||
#search-area input {
|
||||
width: 100%;
|
||||
#filter {
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
#radius-and-unit {
|
||||
width: 6em;
|
||||
float: right;
|
||||
margin-right: -6em;
|
||||
}
|
||||
|
||||
#radius {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
textarea, input {
|
||||
|
|
|
@ -4,6 +4,8 @@ EntityListTool = function(opts) {
|
|||
var url = Script.resolvePath('html/entityList.html');
|
||||
var webView = new WebWindow('Entities', url, 200, 280, true);
|
||||
|
||||
var searchRadius = 100;
|
||||
|
||||
var visible = false;
|
||||
|
||||
webView.setVisible(visible);
|
||||
|
@ -33,7 +35,7 @@ EntityListTool = function(opts) {
|
|||
|
||||
that.sendUpdate = function() {
|
||||
var entities = [];
|
||||
var ids = Entities.findEntities(MyAvatar.position, 100);
|
||||
var ids = Entities.findEntities(MyAvatar.position, searchRadius);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[i];
|
||||
var properties = Entities.getEntityProperties(id);
|
||||
|
@ -80,6 +82,9 @@ EntityListTool = function(opts) {
|
|||
}
|
||||
} else if (data.type == "delete") {
|
||||
deleteSelectedEntities();
|
||||
} else if (data.type === "radius") {
|
||||
searchRadius = data.radius;
|
||||
that.sendUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -443,8 +443,6 @@ Menu::Menu() {
|
|||
SLOT(toggleConnexion(bool)));
|
||||
|
||||
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::HandMouseInput, 0, true);
|
||||
|
|
|
@ -131,8 +131,6 @@ namespace MenuOption {
|
|||
const QString AboutApp = "About Interface";
|
||||
const QString AddRemoveFriends = "Add/Remove Friends...";
|
||||
const QString AddressBar = "Show Address Bar";
|
||||
const QString AlignForearmsWithWrists = "Align Forearms with Wrists";
|
||||
const QString AlternateIK = "Alternate IK";
|
||||
const QString Animations = "Animations...";
|
||||
const QString Atmosphere = "Atmosphere";
|
||||
const QString Attachments = "Attachments...";
|
||||
|
|
|
@ -200,11 +200,9 @@ void Avatar::simulate(float deltaTime) {
|
|||
if (!_shouldRenderBillboard && inViewFrustum) {
|
||||
{
|
||||
PerformanceTimer perfTimer("skeleton");
|
||||
if (_hasNewJointRotations) {
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData.at(i);
|
||||
_skeletonModel.setJointState(i, data.valid, data.rotation);
|
||||
}
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData.at(i);
|
||||
_skeletonModel.setJointState(i, true, data.rotation);
|
||||
}
|
||||
_skeletonModel.simulate(deltaTime, _hasNewJointRotations);
|
||||
simulateAttachments(deltaTime);
|
||||
|
@ -784,8 +782,10 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
|
|||
void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::ivec4& viewport) const {
|
||||
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar();
|
||||
|
||||
// If we have nothing to draw, or it's tottaly transparent, return
|
||||
if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) {
|
||||
// If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return
|
||||
const float CLIP_DISTANCE = 0.2f;
|
||||
if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f
|
||||
|| (glm::dot(frustum.getDirection(), getDisplayNamePosition() - frustum.getPosition()) <= CLIP_DISTANCE)) {
|
||||
return;
|
||||
}
|
||||
auto renderer = textRenderer(DISPLAYNAME);
|
||||
|
|
|
@ -123,18 +123,18 @@ MyAvatar::~MyAvatar() {
|
|||
_lookAtTargetAvatar.reset();
|
||||
}
|
||||
|
||||
QByteArray MyAvatar::toByteArray() {
|
||||
QByteArray MyAvatar::toByteArray(bool cullSmallChanges) {
|
||||
CameraMode mode = Application::getInstance()->getCamera()->getMode();
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
||||
// fake the avatar position that is sent up to the AvatarMixer
|
||||
glm::vec3 oldPosition = _position;
|
||||
_position = getSkeletonPosition();
|
||||
QByteArray array = AvatarData::toByteArray();
|
||||
QByteArray array = AvatarData::toByteArray(cullSmallChanges);
|
||||
// copy the correct position back
|
||||
_position = oldPosition;
|
||||
return array;
|
||||
}
|
||||
return AvatarData::toByteArray();
|
||||
return AvatarData::toByteArray(cullSmallChanges);
|
||||
}
|
||||
|
||||
void MyAvatar::reset() {
|
||||
|
@ -220,7 +220,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
_jointData.resize(_rig->getJointStateCount());
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
JointData& data = _jointData[i];
|
||||
data.valid = _rig->getJointStateRotation(i, data.rotation);
|
||||
_rig->getJointStateRotation(i, data.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -203,7 +203,8 @@ private:
|
|||
glm::vec3 getWorldBodyPosition() const;
|
||||
glm::quat getWorldBodyOrientation() const;
|
||||
|
||||
QByteArray toByteArray();
|
||||
QByteArray toByteArray(bool cullSmallChanges);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
void updateFromTrackers(float deltaTime);
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
|
||||
|
|
|
@ -41,7 +41,7 @@ SkeletonModel::~SkeletonModel() {
|
|||
|
||||
void SkeletonModel::initJointStates(QVector<JointState> states) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
glm::mat4 rootTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
|
||||
int rootJointIndex = geometry.rootJointIndex;
|
||||
int leftHandJointIndex = geometry.leftHandJointIndex;
|
||||
|
@ -51,7 +51,7 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
|
|||
int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1;
|
||||
int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1;
|
||||
|
||||
_rig->initJointStates(states, parentTransform,
|
||||
_rig->initJointStates(states, rootTransform,
|
||||
rootJointIndex,
|
||||
leftHandJointIndex,
|
||||
leftElbowJointIndex,
|
||||
|
@ -83,7 +83,7 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
|
|||
// of its root joint and we need that done before we try to build shapes hence we
|
||||
// recompute all joint transforms at this time.
|
||||
for (int i = 0; i < _rig->getJointStateCount(); i++) {
|
||||
_rig->updateJointState(i, parentTransform);
|
||||
_rig->updateJointState(i, rootTransform);
|
||||
}
|
||||
|
||||
buildShapes();
|
||||
|
@ -157,11 +157,11 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
|
||||
|
||||
Model::simulate(deltaTime, fullUpdate);
|
||||
|
||||
|
||||
if (!isActive() || !_owningAvatar->isMyAvatar()) {
|
||||
return; // only simulate for own avatar
|
||||
}
|
||||
|
||||
|
||||
MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
|
||||
if (myAvatar->isPlaying()) {
|
||||
// Don't take inputs if playing back a recording.
|
||||
|
@ -248,40 +248,24 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
|||
rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector),
|
||||
true, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||
return;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
|
||||
int parentJointIndex = geometry.joints.at(jointIndex).parentIndex;
|
||||
if (parentJointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// rotate palm to align with its normal (normal points out of hand's palm)
|
||||
|
||||
// the palm's position must be transformed into the model-frame
|
||||
glm::quat inverseRotation = glm::inverse(_rotation);
|
||||
glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation);
|
||||
glm::vec3 palmNormal = inverseRotation * palm.getNormal();
|
||||
glm::vec3 fingerDirection = inverseRotation * palm.getFingerDirection();
|
||||
|
||||
glm::quat palmRotation = rotationBetween(geometry.palmDirection, palmNormal);
|
||||
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation;
|
||||
// the palm's "raw" rotation is already in the model-frame
|
||||
glm::quat palmRotation = palm.getRawRotation();
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) {
|
||||
_rig->setHandPosition(jointIndex, palmPosition, palmRotation, extractUniformScale(_scale), PALM_PRIORITY);
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||
float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale);
|
||||
glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f);
|
||||
setJointPosition(parentJointIndex, palmPosition + forearm,
|
||||
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
_rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY);
|
||||
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
||||
_rig->setJointRotationInConstrainedFrame(jointIndex, glm::quat(), PALM_PRIORITY);
|
||||
} else {
|
||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||
}
|
||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
||||
|
@ -301,13 +285,13 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
|||
_rotation :
|
||||
_rotation * _rig->getJointState(joint.parentIndex).getRotation();
|
||||
float fanScale = directionSize * 0.75f;
|
||||
|
||||
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(parentRotation);
|
||||
transform.setScale(fanScale);
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
|
||||
const int AXIS_COUNT = 3;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
@ -318,7 +302,7 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
|||
}
|
||||
glm::vec3 axis;
|
||||
axis[i] = 1.0f;
|
||||
|
||||
|
||||
glm::vec3 otherAxis;
|
||||
if (i == 0) {
|
||||
otherAxis.y = 1.0f;
|
||||
|
@ -339,18 +323,18 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
|||
// better if the skeleton model cached these buffers for each of the joints they are rendering
|
||||
geometryCache->updateVertices(_triangleFanID, points, color);
|
||||
geometryCache->renderVertices(batch, gpu::TRIANGLE_FAN, _triangleFanID);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
renderOrientationDirections(batch, jointIndex, position, _rotation * jointState.getRotation(), directionSize);
|
||||
jointIndex = joint.parentIndex;
|
||||
|
||||
|
||||
} while (jointIndex != -1 && geometry.joints.at(jointIndex).isFree);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointIndex,
|
||||
void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointIndex,
|
||||
glm::vec3 position, const glm::quat& orientation, float size) {
|
||||
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
if (!_jointOrientationLines.contains(jointIndex)) {
|
||||
|
@ -486,7 +470,7 @@ void SkeletonModel::buildShapes() {
|
|||
if (_geometry == NULL || _rig->jointStatesEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (geometry.joints.isEmpty() || geometry.rootJointIndex == -1) {
|
||||
// rootJointIndex == -1 if the avatar model has no skeleton
|
||||
|
@ -550,7 +534,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha
|
|||
geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||
|
||||
// draw a yellow sphere at the capsule bottom point
|
||||
// draw a yellow sphere at the capsule bottom point
|
||||
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f);
|
||||
glm::vec3 axis = topPoint - bottomPoint;
|
||||
transform.setTranslation(bottomPoint);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "AvatarRig.h"
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) {
|
||||
void AvatarRig::updateJointState(int index, glm::mat4 rootTransform) {
|
||||
if (index < 0 && index >= _jointStates.size()) {
|
||||
return; // bail
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) {
|
|||
// compute model transforms
|
||||
if (index == _rootJointIndex) {
|
||||
// we always zero-out the translation part of an avatar's root join-transform.
|
||||
state.computeTransform(parentTransform);
|
||||
state.computeTransform(rootTransform);
|
||||
clearJointTransformTranslation(index);
|
||||
} else {
|
||||
// guard against out-of-bounds access to _jointStates
|
||||
|
|
|
@ -21,7 +21,7 @@ class AvatarRig : public Rig {
|
|||
|
||||
public:
|
||||
~AvatarRig() {}
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||
virtual void updateJointState(int index, glm::mat4 rootTransform);
|
||||
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||
float scale, float priority);
|
||||
};
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
#include "EntityRig.h"
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
void EntityRig::updateJointState(int index, glm::mat4 parentTransform) {
|
||||
void EntityRig::updateJointState(int index, glm::mat4 rootTransform) {
|
||||
JointState& state = _jointStates[index];
|
||||
|
||||
// compute model transforms
|
||||
int parentIndex = state.getParentIndex();
|
||||
if (parentIndex == -1) {
|
||||
state.computeTransform(parentTransform);
|
||||
state.computeTransform(rootTransform);
|
||||
} else {
|
||||
// guard against out-of-bounds access to _jointStates
|
||||
if (parentIndex >= 0 && parentIndex < _jointStates.size()) {
|
||||
|
|
|
@ -21,7 +21,7 @@ class EntityRig : public Rig {
|
|||
|
||||
public:
|
||||
~EntityRig() {}
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||
virtual void updateJointState(int index, glm::mat4 rootTransform);
|
||||
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||
float scale, float priority) {}
|
||||
};
|
||||
|
|
|
@ -153,6 +153,24 @@ void JointState::setRotationInBindFrame(const glm::quat& rotation, float priorit
|
|||
}
|
||||
}
|
||||
|
||||
void JointState::setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority, bool constrain) {
|
||||
// rotation is from bind- to model-frame
|
||||
if (priority >= _animationPriority) {
|
||||
glm::quat parentRotation = computeParentRotation();
|
||||
|
||||
// R = Rp * Rpre * r * Rpost
|
||||
// R' = Rp * Rpre * r' * Rpost
|
||||
// r' = (Rp * Rpre)^ * R' * Rpost^
|
||||
glm::quat targetRotation = glm::inverse(parentRotation * _preRotation) * rotationInModelFrame * glm::inverse(_postRotation);
|
||||
if (constrain && _constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
|
||||
}
|
||||
_rotationInConstrainedFrame = glm::normalize(targetRotation);
|
||||
_transformChanged = true;
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::clearTransformTranslation() {
|
||||
_transform[3][0] = 0.0f;
|
||||
_transform[3][1] = 0.0f;
|
||||
|
|
|
@ -82,6 +82,11 @@ public:
|
|||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
|
||||
|
||||
/// \param rotationInModelRame is in model-frame
|
||||
/// computes and sets new _rotationInConstrainedFrame to match rotationInModelFrame
|
||||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority, bool constrain);
|
||||
|
||||
void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false, float mix = 1.0f);
|
||||
void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation);
|
||||
const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; }
|
||||
|
|
|
@ -194,7 +194,7 @@ void Rig::deleteAnimations() {
|
|||
}
|
||||
}
|
||||
|
||||
void Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
|
||||
void Rig::initJointStates(QVector<JointState> states, glm::mat4 rootTransform,
|
||||
int rootJointIndex,
|
||||
int leftHandJointIndex,
|
||||
int leftElbowJointIndex,
|
||||
|
@ -212,7 +212,7 @@ void Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
|
|||
_rightElbowJointIndex = rightElbowJointIndex;
|
||||
_rightShoulderJointIndex = rightShoulderJointIndex;
|
||||
|
||||
initJointTransforms(parentTransform);
|
||||
initJointTransforms(rootTransform);
|
||||
|
||||
int numStates = _jointStates.size();
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
|
@ -235,14 +235,14 @@ int Rig::indexOfJoint(const QString& jointName) {
|
|||
}
|
||||
|
||||
|
||||
void Rig::initJointTransforms(glm::mat4 parentTransform) {
|
||||
void Rig::initJointTransforms(glm::mat4 rootTransform) {
|
||||
// compute model transforms
|
||||
int numStates = _jointStates.size();
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
JointState& state = _jointStates[i];
|
||||
int parentIndex = state.getParentIndex();
|
||||
if (parentIndex == -1) {
|
||||
state.initTransform(parentTransform);
|
||||
state.initTransform(rootTransform);
|
||||
} else {
|
||||
const JointState& parentState = _jointStates.at(parentIndex);
|
||||
state.initTransform(parentState.getTransform());
|
||||
|
@ -466,7 +466,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
_lastPosition = worldPosition;
|
||||
}
|
||||
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
|
||||
|
||||
if (_enableAnimGraph) {
|
||||
if (!_animNode) {
|
||||
|
@ -489,7 +489,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i, parentTransform);
|
||||
updateJointState(i, rootTransform);
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].resetTransformChanged();
|
||||
|
@ -541,7 +541,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i, parentTransform);
|
||||
updateJointState(i, rootTransform);
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].resetTransformChanged();
|
||||
|
@ -551,7 +551,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
|||
|
||||
bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
|
||||
const QVector<int>& freeLineage, glm::mat4 rootTransform) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -603,7 +603,7 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm:
|
|||
glm::vec3 positionSum;
|
||||
for (int k = j - 1; k > 0; k--) {
|
||||
int index = freeLineage.at(k);
|
||||
updateJointState(index, parentTransform);
|
||||
updateJointState(index, rootTransform);
|
||||
positionSum += extractTranslation(_jointStates.at(index).getTransform());
|
||||
}
|
||||
glm::vec3 projectedCenterOfMass = glm::cross(jointVector,
|
||||
|
@ -626,15 +626,15 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm:
|
|||
|
||||
// now update the joint states from the top
|
||||
for (int j = freeLineage.size() - 1; j >= 0; j--) {
|
||||
updateJointState(freeLineage.at(j), parentTransform);
|
||||
updateJointState(freeLineage.at(j), rootTransform);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform) {
|
||||
// NOTE: targetRotation is from bind- to model-frame
|
||||
const QVector<int>& freeLineage, glm::mat4 rootTransform) {
|
||||
// NOTE: targetRotation is from in model-frame
|
||||
|
||||
if (endIndex == -1 || _jointStates.isEmpty()) {
|
||||
return;
|
||||
|
@ -652,12 +652,27 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
const JointState& state = _jointStates.at(index);
|
||||
int parentIndex = state.getParentIndex();
|
||||
if (parentIndex == -1) {
|
||||
topParentTransform = parentTransform;
|
||||
topParentTransform = rootTransform;
|
||||
} else {
|
||||
topParentTransform = _jointStates[parentIndex].getTransform();
|
||||
}
|
||||
}
|
||||
|
||||
// relax toward default rotation
|
||||
// NOTE: ideally this should use dt and a relaxation timescale to compute how much to relax
|
||||
for (int j = 0; j < numFree; j++) {
|
||||
int nextIndex = freeLineage.at(j);
|
||||
JointState& nextState = _jointStates[nextIndex];
|
||||
if (! nextState.getIsFree()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Apply the zero rotationDelta, but use mixRotationDelta() which blends a bit of the default pose
|
||||
// in the process. This provides stability to the IK solution for most models.
|
||||
float mixFactor = 0.08f;
|
||||
nextState.mixRotationDelta(glm::quat(), mixFactor, priority);
|
||||
}
|
||||
|
||||
// this is a cyclic coordinate descent algorithm: see
|
||||
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
|
||||
|
||||
|
@ -666,7 +681,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
glm::vec3 endPosition = endState.getPosition();
|
||||
float distanceToGo = glm::distance(targetPosition, endPosition);
|
||||
|
||||
const int MAX_ITERATION_COUNT = 2;
|
||||
const int MAX_ITERATION_COUNT = 3;
|
||||
const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm
|
||||
int numIterations = 0;
|
||||
do {
|
||||
|
@ -704,7 +719,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
|
||||
float gravityAngle = glm::angle(gravityDelta);
|
||||
const float MIN_GRAVITY_ANGLE = 0.1f;
|
||||
float mixFactor = 0.5f;
|
||||
float mixFactor = 0.1f;
|
||||
if (gravityAngle < MIN_GRAVITY_ANGLE) {
|
||||
// the final rotation is a mix of the two
|
||||
mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE;
|
||||
|
@ -712,11 +727,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor);
|
||||
}
|
||||
|
||||
// Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose
|
||||
// in the process. This provides stability to the IK solution for most models.
|
||||
// Apply the rotation delta.
|
||||
glm::quat oldNextRotation = nextState.getRotation();
|
||||
float mixFactor = 0.03f;
|
||||
nextState.mixRotationDelta(deltaRotation, mixFactor, priority);
|
||||
float mixFactor = 0.05f;
|
||||
nextState.applyRotationDelta(deltaRotation, mixFactor, priority);
|
||||
|
||||
// measure the result of the rotation which may have been modified by
|
||||
// blending and constraints
|
||||
|
@ -735,10 +749,10 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
|
|||
// measure our success
|
||||
endPosition = endState.getPosition();
|
||||
distanceToGo = glm::distance(targetPosition, endPosition);
|
||||
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR);
|
||||
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo > ACCEPTABLE_IK_ERROR);
|
||||
|
||||
// set final rotation of the end joint
|
||||
endState.setRotationInBindFrame(targetRotation, priority, true);
|
||||
endState.setRotationInModelFrame(targetRotation, priority, true);
|
||||
}
|
||||
|
||||
bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage) {
|
||||
|
|
|
@ -95,7 +95,7 @@ public:
|
|||
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
|
||||
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false);
|
||||
|
||||
void initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
|
||||
void initJointStates(QVector<JointState> states, glm::mat4 rootTransform,
|
||||
int rootJointIndex,
|
||||
int leftHandJointIndex,
|
||||
int leftElbowJointIndex,
|
||||
|
@ -107,7 +107,7 @@ public:
|
|||
int getJointStateCount() const { return _jointStates.size(); }
|
||||
int indexOfJoint(const QString& jointName) ;
|
||||
|
||||
void initJointTransforms(glm::mat4 parentTransform);
|
||||
void initJointTransforms(glm::mat4 rootTransform);
|
||||
void clearJointTransformTranslation(int jointIndex);
|
||||
void reset(const QVector<FBXJoint>& fbxJoints);
|
||||
bool getJointStateRotation(int index, glm::quat& rotation) const;
|
||||
|
@ -138,12 +138,12 @@ public:
|
|||
// Start or stop animations as needed.
|
||||
void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation);
|
||||
// Regardless of who started the animations or how many, update the joints.
|
||||
void updateAnimations(float deltaTime, glm::mat4 parentTransform);
|
||||
void updateAnimations(float deltaTime, glm::mat4 rootTransform);
|
||||
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform);
|
||||
const QVector<int>& freeLineage, glm::mat4 rootTransform);
|
||||
void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform);
|
||||
const QVector<int>& freeLineage, glm::mat4 rootTransform);
|
||||
bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage);
|
||||
float getLimbLength(int jointIndex, const QVector<int>& freeLineage,
|
||||
const glm::vec3 scale, const QVector<FBXJoint>& fbxJoints) const;
|
||||
|
@ -155,7 +155,7 @@ public:
|
|||
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
|
||||
void updateVisibleJointStates();
|
||||
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform) = 0;
|
||||
virtual void updateJointState(int index, glm::mat4 rootTransform) = 0;
|
||||
|
||||
void setEnableRig(bool isEnabled) { _enableRig = isEnabled; }
|
||||
void setEnableAnimGraph(bool isEnabled) { _enableAnimGraph = isEnabled; }
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
|
||||
quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND;
|
||||
|
||||
// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer
|
||||
const float MIN_ROTATION_DOT = 0.9999999f;
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
const glm::vec3 DEFAULT_LOCAL_AABOX_CORNER(-0.5f);
|
||||
|
@ -141,7 +145,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) {
|
|||
_handPosition = glm::inverse(getOrientation()) * (handPosition - _position);
|
||||
}
|
||||
|
||||
QByteArray AvatarData::toByteArray() {
|
||||
QByteArray AvatarData::toByteArray(bool cullSmallChanges) {
|
||||
// TODO: DRY this up to a shared method
|
||||
// that can pack any type given the number of bytes
|
||||
// and return the number of bytes to push the pointer
|
||||
|
@ -234,11 +238,19 @@ QByteArray AvatarData::toByteArray() {
|
|||
|
||||
// joint data
|
||||
*destinationBuffer++ = _jointData.size();
|
||||
unsigned char* validityPosition = destinationBuffer;
|
||||
unsigned char validity = 0;
|
||||
int validityBit = 0;
|
||||
foreach (const JointData& data, _jointData) {
|
||||
if (data.valid) {
|
||||
validity |= (1 << validityBit);
|
||||
|
||||
_lastSentJointData.resize(_jointData.size());
|
||||
|
||||
// foreach (const JointData& data, _jointData) {
|
||||
for (int i=0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData.at(i);
|
||||
if (_lastSentJointData[i].rotation != data.rotation) {
|
||||
if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) {
|
||||
validity |= (1 << validityBit);
|
||||
}
|
||||
}
|
||||
if (++validityBit == BITS_IN_BYTE) {
|
||||
*destinationBuffer++ = validity;
|
||||
|
@ -248,9 +260,18 @@ QByteArray AvatarData::toByteArray() {
|
|||
if (validityBit != 0) {
|
||||
*destinationBuffer++ = validity;
|
||||
}
|
||||
foreach (const JointData& data, _jointData) {
|
||||
if (data.valid) {
|
||||
|
||||
validityBit = 0;
|
||||
validity = *validityPosition++;
|
||||
for (int i = 0; i < _jointData.size(); i ++) {
|
||||
const JointData& data = _jointData[ i ];
|
||||
if (validity & (1 << validityBit)) {
|
||||
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation);
|
||||
_lastSentJointData[i].rotation = data.rotation;
|
||||
}
|
||||
if (++validityBit == BITS_IN_BYTE) {
|
||||
validityBit = 0;
|
||||
validity = *validityPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -494,6 +515,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
int numValidJoints = 0;
|
||||
_jointData.resize(numJoints);
|
||||
|
||||
QVector<bool> valids;
|
||||
valids.resize(numJoints);
|
||||
|
||||
{ // validity bits
|
||||
unsigned char validity = 0;
|
||||
int validityBit = 0;
|
||||
|
@ -505,7 +530,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
if (valid) {
|
||||
++numValidJoints;
|
||||
}
|
||||
_jointData[i].valid = valid;
|
||||
valids[i] = valid;
|
||||
validityBit = (validityBit + 1) % BITS_IN_BYTE;
|
||||
}
|
||||
}
|
||||
|
@ -527,7 +552,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
{ // joint data
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
JointData& data = _jointData[i];
|
||||
if (data.valid) {
|
||||
if (valids[i]) {
|
||||
_hasNewJointRotations = true;
|
||||
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation);
|
||||
}
|
||||
|
@ -731,7 +756,6 @@ void AvatarData::setJointData(int index, const glm::quat& rotation) {
|
|||
_jointData.resize(index + 1);
|
||||
}
|
||||
JointData& data = _jointData[index];
|
||||
data.valid = true;
|
||||
data.rotation = rotation;
|
||||
}
|
||||
|
||||
|
@ -746,7 +770,6 @@ void AvatarData::clearJointData(int index) {
|
|||
if (_jointData.size() <= index) {
|
||||
_jointData.resize(index + 1);
|
||||
}
|
||||
_jointData[index].valid = false;
|
||||
}
|
||||
|
||||
bool AvatarData::isJointDataValid(int index) const {
|
||||
|
@ -759,7 +782,7 @@ bool AvatarData::isJointDataValid(int index) const {
|
|||
Q_RETURN_ARG(bool, result), Q_ARG(int, index));
|
||||
return result;
|
||||
}
|
||||
return index < _jointData.size() && _jointData.at(index).valid;
|
||||
return index < _jointData.size();
|
||||
}
|
||||
|
||||
glm::quat AvatarData::getJointRotation(int index) const {
|
||||
|
@ -1060,7 +1083,7 @@ void AvatarData::setJointMappingsFromNetworkReply() {
|
|||
void AvatarData::sendAvatarDataPacket() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QByteArray avatarByteArray = toByteArray();
|
||||
QByteArray avatarByteArray = toByteArray(true);
|
||||
|
||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size());
|
||||
avatarPacket->write(avatarByteArray);
|
||||
|
|
|
@ -171,7 +171,7 @@ public:
|
|||
glm::vec3 getHandPosition() const;
|
||||
void setHandPosition(const glm::vec3& handPosition);
|
||||
|
||||
virtual QByteArray toByteArray();
|
||||
virtual QByteArray toByteArray(bool cullSmallChanges);
|
||||
|
||||
/// \return true if an error should be logged
|
||||
bool shouldLogError(const quint64& now);
|
||||
|
@ -357,6 +357,7 @@ protected:
|
|||
char _handState;
|
||||
|
||||
QVector<JointData> _jointData; ///< the state of the skeleton joints
|
||||
QVector<JointData> _lastSentJointData; ///< the state of the skeleton joints last time we transmitted
|
||||
|
||||
// key state
|
||||
KeyState _keyState;
|
||||
|
@ -408,7 +409,6 @@ Q_DECLARE_METATYPE(AvatarData*)
|
|||
|
||||
class JointData {
|
||||
public:
|
||||
bool valid;
|
||||
glm::quat rotation;
|
||||
};
|
||||
|
||||
|
|
|
@ -104,10 +104,10 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
|||
if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,20 +131,20 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() {
|
|||
|
||||
float maxVelocityX = fabsf(_velocity.x) + _velocitySpread.x;
|
||||
float maxAccelerationX = fabsf(_acceleration.x) + _accelerationSpread.x;
|
||||
float maxXDistance = (maxVelocityX * time) + (0.5 * maxAccelerationX * time * time);
|
||||
float maxXDistance = (maxVelocityX * time) + (0.5f * maxAccelerationX * time * time);
|
||||
|
||||
float maxVelocityY = fabs(_velocity.y) + _velocitySpread.y;
|
||||
float maxVelocityY = fabsf(_velocity.y) + _velocitySpread.y;
|
||||
float maxAccelerationY = fabsf(_acceleration.y) + _accelerationSpread.y;
|
||||
float maxYDistance = (maxVelocityY * time) + (0.5 * maxAccelerationY * time * time);
|
||||
float maxYDistance = (maxVelocityY * time) + (0.5f * maxAccelerationY * time * time);
|
||||
|
||||
float maxVelocityZ = fabsf(_velocity.z) + _velocitySpread.z;
|
||||
float maxAccelerationZ = fabsf(_acceleration.z) + _accelerationSpread.z;
|
||||
float maxZDistance = (maxVelocityZ * time) + (0.5 * maxAccelerationZ * time * time);
|
||||
float maxZDistance = (maxVelocityZ * time) + (0.5f * maxAccelerationZ * time * time);
|
||||
|
||||
float maxDistance = std::max(maxXDistance, std::max(maxYDistance, maxZDistance));
|
||||
|
||||
//times 2 because dimensions are diameters not radii
|
||||
glm::vec3 dims(2.0 * maxDistance);
|
||||
glm::vec3 dims(2.0f * maxDistance);
|
||||
EntityItem::setDimensions(dims);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
#include "SixenseManager.h"
|
||||
#include "UserActivityLogger.h"
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
#include "sixense.h"
|
||||
#endif
|
||||
|
||||
// TODO: This should not be here
|
||||
#include <QLoggingCategory>
|
||||
Q_DECLARE_LOGGING_CATEGORY(inputplugins)
|
||||
|
@ -45,12 +49,14 @@ const float NECK_Z = 0.3f; // meters
|
|||
|
||||
const float CONTROLLER_THRESHOLD = 0.35f;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
typedef int (*SixenseBaseFunction)();
|
||||
typedef int (*SixenseTakeIntFunction)(int);
|
||||
#ifdef HAVE_SIXENSE
|
||||
typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
const QString SixenseManager::NAME = "Sixense";
|
||||
|
@ -66,8 +72,8 @@ SixenseManager& SixenseManager::getInstance() {
|
|||
}
|
||||
|
||||
SixenseManager::SixenseManager() :
|
||||
InputDevice("Hydra"),
|
||||
#if defined(HAVE_SIXENSE) && defined(__APPLE__)
|
||||
InputDevice("Hydra"),
|
||||
#ifdef __APPLE__
|
||||
_sixenseLibrary(NULL),
|
||||
#endif
|
||||
_hydrasConnected(false)
|
||||
|
@ -213,18 +219,16 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
|
|||
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
||||
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
||||
position *= METERS_PER_MILLIMETER;
|
||||
|
||||
|
||||
// Check to see if this hand/controller is on the base
|
||||
const float CONTROLLER_AT_BASE_DISTANCE = 0.075f;
|
||||
if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) {
|
||||
handleButtonEvent(data->buttons, numActiveControllers - 1);
|
||||
handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1);
|
||||
|
||||
// Rotation of Palm
|
||||
glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
|
||||
rotation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)) * _orbRotation * rotation;
|
||||
|
||||
if (!jointsCaptured) {
|
||||
// Rotation of Palm
|
||||
glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]);
|
||||
handlePoseEvent(position, rotation, numActiveControllers - 1);
|
||||
} else {
|
||||
_poseStateMap.clear();
|
||||
|
@ -232,7 +236,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
|
|||
} else {
|
||||
_poseStateMap[(numActiveControllers - 1) == 0 ? LEFT_HAND : RIGHT_HAND] = UserInputMapper::PoseValue();
|
||||
}
|
||||
|
||||
|
||||
// // Read controller buttons and joystick into the hand
|
||||
// palm->setControllerButtons(data->buttons);
|
||||
// palm->setTrigger(data->trigger);
|
||||
|
@ -242,7 +246,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
|
|||
if (numActiveControllers == 2) {
|
||||
updateCalibration(controllers);
|
||||
}
|
||||
|
||||
|
||||
for (auto axisState : _axisStateMap) {
|
||||
if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) {
|
||||
_axisStateMap[axisState.first] = 0.0f;
|
||||
|
@ -436,16 +440,66 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, int index) {
|
|||
|
||||
void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int index) {
|
||||
#ifdef HAVE_SIXENSE
|
||||
// From ABOVE the sixense coordinate frame looks like this:
|
||||
//
|
||||
// |
|
||||
// USB cables
|
||||
// |
|
||||
// .-. user
|
||||
// (Orb) --neckX---- forward
|
||||
// '-' |
|
||||
// | | user
|
||||
// neckZ y +---- right
|
||||
// | (o)-----x
|
||||
// |
|
||||
// |
|
||||
// z
|
||||
|
||||
// Transform the measured position into body frame.
|
||||
glm::vec3 neck = _neckBase;
|
||||
// Set y component of the "neck" to raise the measured position a little bit.
|
||||
neck.y = 0.5f;
|
||||
position = _orbRotation * (position - neck);
|
||||
|
||||
// adjustment for hydra controllers fit into hands
|
||||
float sign = (index == 0) ? -1.0f : 1.0f;
|
||||
rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
|
||||
// From ABOVE the hand canonical axes looks like this:
|
||||
//
|
||||
// | | | | y | | | |
|
||||
// | | | | | | | | |
|
||||
// | | | | |
|
||||
// |left | / x----(+) \ |right|
|
||||
// | _/ z \_ |
|
||||
// | | | |
|
||||
// | | | |
|
||||
//
|
||||
|
||||
// To convert sixense's delta-rotation into the hand's frame we will have to transform it like so:
|
||||
//
|
||||
// deltaHand = Qsh^ * deltaSixense * Qsh
|
||||
//
|
||||
// where Qsh = transform from sixense axes to hand axes. By inspection we can determine Qsh:
|
||||
//
|
||||
// Qsh = angleAxis(PI, zAxis) * angleAxis(-PI/2, xAxis)
|
||||
//
|
||||
const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
const glm::quat sixenseToHand = glm::angleAxis(PI, zAxis) * glm::angleAxis(-PI/2.0f, xAxis);
|
||||
|
||||
// In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers
|
||||
// and how they fit into the hand in their relaxed state. This offset is a quarter turn about
|
||||
// the sixense's z-axis, with its direction different for the two hands:
|
||||
float sign = (index == 0) ? 1.0f : -1.0f;
|
||||
const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, zAxis);
|
||||
|
||||
// Finally, there is a post-offset (same for both hands) to get the hand's rest orientation
|
||||
// (fingers forward, palm down) aligned properly in the avatar's model-frame.
|
||||
const glm::quat postOffset = glm::angleAxis(PI / 2.0f, xAxis);
|
||||
|
||||
// The total rotation of the hand uses the formula:
|
||||
//
|
||||
// rotation = postOffset * Qsh^ * (measuredRotation * preOffset) * Qsh
|
||||
//
|
||||
rotation = postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
|
||||
|
||||
_poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
|
||||
#endif // HAVE_SIXENSE
|
||||
}
|
||||
|
@ -453,7 +507,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
|
|||
void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) {
|
||||
// Grab the current free device ID
|
||||
_deviceID = mapper.getFreeDeviceID();
|
||||
|
||||
|
||||
auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
|
||||
proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
|
||||
proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
|
||||
|
@ -465,25 +519,25 @@ void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) {
|
|||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 0), "Left Trigger Press"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2"));
|
||||
|
||||
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down"));
|
||||
availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right"));
|
||||
|
@ -506,35 +560,35 @@ void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
|
|||
const float JOYSTICK_PITCH_SPEED = 0.25f;
|
||||
const float BUTTON_MOVE_SPEED = 1.0f;
|
||||
const float BOOM_SPEED = 0.1f;
|
||||
|
||||
|
||||
// Left Joystick: Movement, strafing
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED);
|
||||
|
||||
|
||||
// Right Joystick: Camera orientation
|
||||
mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED);
|
||||
|
||||
|
||||
// Buttons
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED);
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED);
|
||||
mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED);
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0));
|
||||
mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1));
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0));
|
||||
mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1));
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
|
||||
mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
|
||||
|
||||
|
||||
mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
|
||||
mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ void UserInputMapper::update(float deltaTime) {
|
|||
for (auto i = 0; i < NUM_ACTIONS; i++) {
|
||||
_actionStates[i] *= _actionScales[i];
|
||||
// Emit only on change, and emit when moving back to 0
|
||||
if (fabs(_actionStates[i] - _lastActionStates[i]) > EPSILON) {
|
||||
if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) {
|
||||
_lastActionStates[i] = _actionStates[i];
|
||||
emit actionEvent(i, _actionStates[i]);
|
||||
}
|
||||
|
@ -319,4 +319,4 @@ void UserInputMapper::createActionNames() {
|
|||
_actionNames[SHIFT] = "SHIFT";
|
||||
_actionNames[ACTION1] = "ACTION1";
|
||||
_actionNames[ACTION2] = "ACTION2";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -324,9 +324,64 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) {
|
|||
glm::vec3 position = extractTranslation(mat);
|
||||
glm::quat rotation = glm::quat_cast(mat);
|
||||
|
||||
// Flip the rotation appropriately for each hand
|
||||
int sign = index == LEFT_HAND ? 1 : -1;
|
||||
rotation = rotation * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
// When the sensor-to-world rotation is identity the coordinate axes look like this:
|
||||
//
|
||||
// user
|
||||
// forward
|
||||
// z
|
||||
// |
|
||||
// y| user
|
||||
// y o----x right
|
||||
// o-----x user
|
||||
// | up
|
||||
// |
|
||||
// z
|
||||
//
|
||||
// Vive
|
||||
//
|
||||
|
||||
// From ABOVE the hand canonical axes looks like this:
|
||||
//
|
||||
// | | | | y | | | |
|
||||
// | | | | | | | | |
|
||||
// | | | | |
|
||||
// |left | / x---- + \ |right|
|
||||
// | _/ z \_ |
|
||||
// | | | |
|
||||
// | | | |
|
||||
//
|
||||
|
||||
// So when the user is standing in Vive space facing the -zAxis with hands outstretched and palms down
|
||||
// the rotation to align the Vive axes with those of the hands is:
|
||||
//
|
||||
// QviveToHand = halfTurnAboutY * quaterTurnAboutX
|
||||
|
||||
// Due to how the Vive controllers fit into the palm there is an offset that is different for each hand.
|
||||
// You can think of this offset as the inverse of the measured rotation when the hands are posed, such that
|
||||
// the combination (measurement * offset) is identity at this orientation.
|
||||
//
|
||||
// Qoffset = glm::inverse(deltaRotation when hand is posed fingers forward, palm down)
|
||||
//
|
||||
// An approximate offset for the Vive can be obtained by inpection:
|
||||
//
|
||||
// Qoffset = glm::inverse(glm::angleAxis(sign * PI/4.0f, zAxis) * glm::angleAxis(PI/2.0f, xAxis))
|
||||
//
|
||||
|
||||
// Finally there is another flip around the yAxis to re-align from model to Vive space, so the full equation is:
|
||||
//
|
||||
// Q = yFlip * combinedMeasurement * viveToHand
|
||||
//
|
||||
// Q = yFlip * (deltaQ * QOffset) * (yFlip * quarterTurnAboutX)
|
||||
//
|
||||
// Q = yFlip * (deltaQ * inverse(deltaQForAlignedHand)) * (yFlip * quarterTurnAboutX)
|
||||
|
||||
const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
float sign = (index == LEFT_HAND) ? -1.0f : 1.0f;
|
||||
const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
const glm::quat offset = glm::inverse(signedQuaterZ * eighthX);
|
||||
rotation = yFlip * rotation * offset * yFlip * quarterX;
|
||||
|
||||
position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET);
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ PacketVersion versionForPacketType(PacketType::Value packetType) {
|
|||
case EntityData:
|
||||
return VERSION_ENTITIES_PARTICLE_MODIFICATIONS;
|
||||
case AvatarData:
|
||||
return 12;
|
||||
return 13;
|
||||
default:
|
||||
return 11;
|
||||
}
|
||||
|
|
|
@ -243,7 +243,7 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
|
|||
batch._glUniform2f(_depthTexCoordScaleLoc, depthTexCoordScaleS, depthTexCoordScaleT);
|
||||
|
||||
batch._glUniform2f(_renderTargetResLoc, fbWidth, fbHeight);
|
||||
batch._glUniform2f(_renderTargetResInvLoc, 1.0/fbWidth, 1.0/fbHeight);
|
||||
batch._glUniform2f(_renderTargetResInvLoc, 1.0f / fbWidth, 1.0f / fbHeight);
|
||||
|
||||
glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||
|
|
|
@ -599,7 +599,7 @@ void ScriptEngine::run() {
|
|||
/ (1000 * 1000)) + 0.5);
|
||||
const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t);
|
||||
|
||||
QByteArray avatarByteArray = _avatarData->toByteArray();
|
||||
QByteArray avatarByteArray = _avatarData->toByteArray(true);
|
||||
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size());
|
||||
|
||||
avatarPacket->write(avatarByteArray);
|
||||
|
|
|
@ -372,20 +372,20 @@ QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) {
|
|||
// create matrix from orientation and position
|
||||
glm::mat4 createMatFromQuatAndPos(const glm::quat& q, const glm::vec3& p) {
|
||||
glm::mat4 m = glm::mat4_cast(q);
|
||||
m[3] = glm::vec4(p, 1);
|
||||
m[3] = glm::vec4(p, 1.0f);
|
||||
return m;
|
||||
}
|
||||
|
||||
// cancel out roll and pitch
|
||||
glm::quat cancelOutRollAndPitch(const glm::quat& q) {
|
||||
glm::vec3 zAxis = q * glm::vec3(0, 0, 1);
|
||||
glm::vec3 zAxis = q * glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
|
||||
// cancel out the roll and pitch
|
||||
glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0) ? vec3(1, 0, 0) : glm::normalize(vec3(zAxis.x, 0, zAxis.z));
|
||||
glm::vec3 newX = glm::cross(vec3(0, 1, 0), newZ);
|
||||
glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0.0f) ? vec3(1.0f, 0.0f, 0.0f) : glm::normalize(vec3(zAxis.x, 0.0f, zAxis.z));
|
||||
glm::vec3 newX = glm::cross(vec3(0.0f, 1.0f, 0.0f), newZ);
|
||||
glm::vec3 newY = glm::cross(newZ, newX);
|
||||
|
||||
glm::mat4 temp(glm::vec4(newX, 0), glm::vec4(newY, 0), glm::vec4(newZ, 0), glm::vec4(0, 0, 0, 1));
|
||||
glm::mat4 temp(glm::vec4(newX, 0.0f), glm::vec4(newY, 0.0f), glm::vec4(newZ, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
return glm::quat_cast(temp);
|
||||
}
|
||||
|
||||
|
@ -394,16 +394,16 @@ glm::mat4 cancelOutRollAndPitch(const glm::mat4& m) {
|
|||
glm::vec3 zAxis = glm::vec3(m[2]);
|
||||
|
||||
// cancel out the roll and pitch
|
||||
glm::vec3 newZ = (zAxis.x == 0 && zAxis.z == 0) ? vec3(1, 0, 0) : glm::normalize(vec3(zAxis.x, 0, zAxis.z));
|
||||
glm::vec3 newX = glm::cross(vec3(0, 1, 0), newZ);
|
||||
glm::vec3 newZ = (zAxis.x == 0.0f && zAxis.z == 0.0f) ? vec3(1.0f, 0.0f, 0.0f) : glm::normalize(vec3(zAxis.x, 0.0f, zAxis.z));
|
||||
glm::vec3 newX = glm::cross(vec3(0.0f, 1.0f, 0.0f), newZ);
|
||||
glm::vec3 newY = glm::cross(newZ, newX);
|
||||
|
||||
glm::mat4 temp(glm::vec4(newX, 0), glm::vec4(newY, 0), glm::vec4(newZ, 0), m[3]);
|
||||
glm::mat4 temp(glm::vec4(newX, 0.0f), glm::vec4(newY, 0.0f), glm::vec4(newZ, 0.0f), m[3]);
|
||||
return temp;
|
||||
}
|
||||
|
||||
glm::vec3 transformPoint(const glm::mat4& m, const glm::vec3& p) {
|
||||
glm::vec4 temp = m * glm::vec4(p, 1);
|
||||
glm::vec4 temp = m * glm::vec4(p, 1.0f);
|
||||
return glm::vec3(temp.x / temp.w, temp.y / temp.w, temp.z / temp.w);
|
||||
}
|
||||
|
||||
|
|
|
@ -93,8 +93,8 @@ template <typename T>
|
|||
void testByteCountCoded() {
|
||||
testByteCountCodedStable<T>(0);
|
||||
testByteCountCodedStable<T>(1);
|
||||
testByteCountCodedStable<T>(1 << 16);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 16);
|
||||
testByteCountCodedStable<T>(1 << 8*sizeof(T));
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 8*sizeof(T));
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 8);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 1);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max());
|
||||
|
|
|
@ -342,8 +342,8 @@ public:
|
|||
glm::vec3 unitscale { 1.0f };
|
||||
glm::vec3 up { 0.0f, 1.0f, 0.0f };
|
||||
|
||||
glm::vec3 cam_pos { 1.5f * sin(t), 0.0f, 2.0f };
|
||||
// glm::vec3 camera_focus { 5.0f * cos(t * 0.1f), 0.0f, 0.0f };
|
||||
glm::vec3 cam_pos { 1.5f * sinf(t), 0.0f, 2.0f };
|
||||
// glm::vec3 camera_focus { 5.0f * cosf(t * 0.1f), 0.0f, 0.0f };
|
||||
glm::vec3 camera_focus { 0.0f, 0.0f, 0.0f };
|
||||
glm::quat cam_rotation;
|
||||
// glm::quat cam_rotation = glm::quat_cast(glm::lookAt(cam_pos, camera_focus, up));
|
||||
|
|
Loading…
Reference in a new issue