mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 20:48:56 +02:00
Merge pull request #2793 from PhilipRosedale/master
Further improvements to avatar eye contact based on head direction
This commit is contained in:
commit
7a191961cf
8 changed files with 60 additions and 37 deletions
|
@ -37,6 +37,7 @@ var radiusMinimum = 0.05;
|
||||||
var radiusMaximum = 0.5;
|
var radiusMaximum = 0.5;
|
||||||
|
|
||||||
var modelURLs = [
|
var modelURLs = [
|
||||||
|
"https://s3-us-west-1.amazonaws.com/highfidelity-public/models/music/EVHFrankenstein.fbx",
|
||||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
|
||||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx",
|
||||||
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
|
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/pug.fbx",
|
||||||
|
@ -51,6 +52,12 @@ var currentModelURL = 1;
|
||||||
var numModels = modelURLs.length;
|
var numModels = modelURLs.length;
|
||||||
|
|
||||||
|
|
||||||
|
function getNewVoxelPosition() {
|
||||||
|
var camera = Camera.getPosition();
|
||||||
|
var forwardVector = Quat.getFront(MyAvatar.orientation);
|
||||||
|
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, 2.0));
|
||||||
|
return newPosition;
|
||||||
|
}
|
||||||
|
|
||||||
function keyPressEvent(event) {
|
function keyPressEvent(event) {
|
||||||
debugPrint("event.text=" + event.text);
|
debugPrint("event.text=" + event.text);
|
||||||
|
@ -63,7 +70,20 @@ function keyPressEvent(event) {
|
||||||
rightRecentlyDeleted = false;
|
rightRecentlyDeleted = false;
|
||||||
rightModelAlreadyInHand = false;
|
rightModelAlreadyInHand = false;
|
||||||
}
|
}
|
||||||
|
} else if (event.text == "m") {
|
||||||
|
var URL = Window.prompt("Model URL", "Enter URL, e.g. http://foo.com/model.fbx");
|
||||||
|
Window.alert("Your response was: " + prompt);
|
||||||
|
var modelPosition = getNewVoxelPosition();
|
||||||
|
var properties = { position: { x: modelPosition.x,
|
||||||
|
y: modelPosition.y,
|
||||||
|
z: modelPosition.z },
|
||||||
|
radius: modelRadius,
|
||||||
|
modelURL: URL
|
||||||
|
};
|
||||||
|
newModel = Models.addModel(properties);
|
||||||
|
|
||||||
} else if (event.text == "DELETE" || event.text == "BACKSPACE") {
|
} else if (event.text == "DELETE" || event.text == "BACKSPACE") {
|
||||||
|
|
||||||
if (leftModelAlreadyInHand) {
|
if (leftModelAlreadyInHand) {
|
||||||
print("want to delete leftHandModel=" + leftHandModel);
|
print("want to delete leftHandModel=" + leftHandModel);
|
||||||
Models.deleteModel(leftHandModel);
|
Models.deleteModel(leftHandModel);
|
||||||
|
|
|
@ -488,6 +488,7 @@ void Menu::loadSettings(QSettings* settings) {
|
||||||
|
|
||||||
_audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0);
|
_audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0);
|
||||||
_fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES);
|
_fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES);
|
||||||
|
_realWorldFieldOfView = loadSetting(settings, "realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES);
|
||||||
_faceshiftEyeDeflection = loadSetting(settings, "faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION);
|
_faceshiftEyeDeflection = loadSetting(settings, "faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION);
|
||||||
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
|
_maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM);
|
||||||
_maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS);
|
_maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS);
|
||||||
|
|
|
@ -85,6 +85,9 @@ public:
|
||||||
void setAudioJitterBufferSamples(float audioJitterBufferSamples) { _audioJitterBufferSamples = audioJitterBufferSamples; }
|
void setAudioJitterBufferSamples(float audioJitterBufferSamples) { _audioJitterBufferSamples = audioJitterBufferSamples; }
|
||||||
float getFieldOfView() const { return _fieldOfView; }
|
float getFieldOfView() const { return _fieldOfView; }
|
||||||
void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; }
|
void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; }
|
||||||
|
float getRealWorldFieldOfView() const { return _realWorldFieldOfView; }
|
||||||
|
void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; }
|
||||||
|
|
||||||
float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; }
|
float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; }
|
||||||
void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; }
|
void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; }
|
||||||
QString getSnapshotsLocation() const;
|
QString getSnapshotsLocation() const;
|
||||||
|
@ -230,6 +233,7 @@ private:
|
||||||
int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback
|
int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback
|
||||||
BandwidthDialog* _bandwidthDialog;
|
BandwidthDialog* _bandwidthDialog;
|
||||||
float _fieldOfView; /// in Degrees, doesn't apply to HMD like Oculus
|
float _fieldOfView; /// in Degrees, doesn't apply to HMD like Oculus
|
||||||
|
float _realWorldFieldOfView; // The actual FOV set by the user's monitor size and view distance
|
||||||
float _faceshiftEyeDeflection;
|
float _faceshiftEyeDeflection;
|
||||||
FrustumDrawMode _frustumDrawMode;
|
FrustumDrawMode _frustumDrawMode;
|
||||||
ViewFrustumOffset _viewFrustumOffset;
|
ViewFrustumOffset _viewFrustumOffset;
|
||||||
|
|
|
@ -233,6 +233,17 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
|
||||||
_skeletonModel.renderBoundingCollisionShapes(0.7f);
|
_skeletonModel.renderBoundingCollisionShapes(0.7f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If this is the avatar being looked at, render a little ball above their head
|
||||||
|
if (_isLookAtTarget) {
|
||||||
|
const float LOOK_AT_INDICATOR_RADIUS = 0.03f;
|
||||||
|
const float LOOK_AT_INDICATOR_HEIGHT = 0.60f;
|
||||||
|
const float LOOK_AT_INDICATOR_COLOR[] = { 0.8f, 0.0f, 0.0f, 0.5f };
|
||||||
|
glPushMatrix();
|
||||||
|
glColor4fv(LOOK_AT_INDICATOR_COLOR);
|
||||||
|
glTranslatef(_position.x, _position.y + (getSkeletonHeight() * LOOK_AT_INDICATOR_HEIGHT), _position.z);
|
||||||
|
glutSolidSphere(LOOK_AT_INDICATOR_RADIUS, 15, 15);
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
// quick check before falling into the code below:
|
// quick check before falling into the code below:
|
||||||
// (a 10 degree breadth of an almost 2 meter avatar kicks in at about 12m)
|
// (a 10 degree breadth of an almost 2 meter avatar kicks in at about 12m)
|
||||||
|
|
|
@ -79,7 +79,7 @@ public:
|
||||||
//setters
|
//setters
|
||||||
void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
|
void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
|
||||||
void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction);
|
void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction);
|
||||||
|
void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; }
|
||||||
//getters
|
//getters
|
||||||
bool isInitialized() const { return _initialized; }
|
bool isInitialized() const { return _initialized; }
|
||||||
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
|
SkeletonModel& getSkeletonModel() { return _skeletonModel; }
|
||||||
|
@ -200,6 +200,7 @@ private:
|
||||||
bool _initialized;
|
bool _initialized;
|
||||||
QScopedPointer<Texture> _billboardTexture;
|
QScopedPointer<Texture> _billboardTexture;
|
||||||
bool _shouldRenderBillboard;
|
bool _shouldRenderBillboard;
|
||||||
|
bool _isLookAtTarget;
|
||||||
|
|
||||||
void renderBillboard();
|
void renderBillboard();
|
||||||
|
|
||||||
|
|
|
@ -249,14 +249,17 @@ void MyAvatar::updateFromGyros(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the rotation of the avatar's head (as seen by others, not affecting view frustum)
|
// Set the rotation of the avatar's head (as seen by others, not affecting view frustum)
|
||||||
// to be scaled. Pitch is greater to emphasize nodding behavior / synchrony.
|
// to be scaled such that when the user's physical head is pointing at edge of screen, the
|
||||||
const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f;
|
// avatar head is at the edge of the in-world view frustum. So while a real person may move
|
||||||
const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f;
|
// their head only 30 degrees or so, this may correspond to a 90 degree field of view.
|
||||||
const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f;
|
// Note that roll is magnified by a constant because it is not related to field of view.
|
||||||
|
|
||||||
|
float magnifyFieldOfView = Menu::getInstance()->getFieldOfView() / Menu::getInstance()->getRealWorldFieldOfView();
|
||||||
|
|
||||||
Head* head = getHead();
|
Head* head = getHead();
|
||||||
head->setDeltaPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
head->setDeltaPitch(estimatedRotation.x * magnifyFieldOfView);
|
||||||
head->setDeltaYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
head->setDeltaYaw(estimatedRotation.y * magnifyFieldOfView);
|
||||||
head->setDeltaRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
head->setDeltaRoll(estimatedRotation.z);
|
||||||
|
|
||||||
// Update torso lean distance based on accelerometer data
|
// Update torso lean distance based on accelerometer data
|
||||||
const float TORSO_LENGTH = 0.5f;
|
const float TORSO_LENGTH = 0.5f;
|
||||||
|
@ -339,18 +342,15 @@ void MyAvatar::renderHeadMouse(int screenWidth, int screenHeight) const {
|
||||||
|
|
||||||
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
Faceshift* faceshift = Application::getInstance()->getFaceshift();
|
||||||
|
|
||||||
|
float pixelsPerDegree = screenHeight / Menu::getInstance()->getFieldOfView();
|
||||||
|
|
||||||
// Display small target box at center or head mouse target that can also be used to measure LOD
|
// Display small target box at center or head mouse target that can also be used to measure LOD
|
||||||
float headPitch = getHead()->getFinalPitch();
|
float headPitch = getHead()->getFinalPitch();
|
||||||
float headYaw = getHead()->getFinalYaw();
|
float headYaw = getHead()->getFinalYaw();
|
||||||
//
|
|
||||||
// It should be noted that the following constant is a function
|
|
||||||
// how far the viewer's head is away from both the screen and the size of the screen,
|
|
||||||
// which are both things we cannot know without adding a calibration phase.
|
|
||||||
//
|
|
||||||
const float PIXELS_PER_VERTICAL_DEGREE = 20.0f;
|
|
||||||
float aspectRatio = (float) screenWidth / (float) screenHeight;
|
float aspectRatio = (float) screenWidth / (float) screenHeight;
|
||||||
int headMouseX = screenWidth / 2.f - headYaw * aspectRatio * PIXELS_PER_VERTICAL_DEGREE;
|
int headMouseX = screenWidth / 2.f - headYaw * aspectRatio * pixelsPerDegree;
|
||||||
int headMouseY = screenHeight / 2.f - headPitch * PIXELS_PER_VERTICAL_DEGREE;
|
int headMouseY = screenHeight / 2.f - headPitch * pixelsPerDegree;
|
||||||
|
|
||||||
glColor3f(1.0f, 1.0f, 1.0f);
|
glColor3f(1.0f, 1.0f, 1.0f);
|
||||||
glDisable(GL_LINE_SMOOTH);
|
glDisable(GL_LINE_SMOOTH);
|
||||||
|
@ -367,8 +367,8 @@ void MyAvatar::renderHeadMouse(int screenWidth, int screenHeight) const {
|
||||||
|
|
||||||
float avgEyePitch = faceshift->getEstimatedEyePitch();
|
float avgEyePitch = faceshift->getEstimatedEyePitch();
|
||||||
float avgEyeYaw = faceshift->getEstimatedEyeYaw();
|
float avgEyeYaw = faceshift->getEstimatedEyeYaw();
|
||||||
int eyeTargetX = (screenWidth / 2) - avgEyeYaw * aspectRatio * PIXELS_PER_VERTICAL_DEGREE;
|
int eyeTargetX = (screenWidth / 2) - avgEyeYaw * aspectRatio * pixelsPerDegree;
|
||||||
int eyeTargetY = (screenHeight / 2) - avgEyePitch * PIXELS_PER_VERTICAL_DEGREE;
|
int eyeTargetY = (screenHeight / 2) - avgEyePitch * pixelsPerDegree;
|
||||||
|
|
||||||
glColor3f(0.0f, 1.0f, 1.0f);
|
glColor3f(0.0f, 1.0f, 1.0f);
|
||||||
glDisable(GL_LINE_SMOOTH);
|
glDisable(GL_LINE_SMOOTH);
|
||||||
|
@ -509,23 +509,6 @@ void MyAvatar::sendKillAvatar() {
|
||||||
NodeList::getInstance()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer);
|
NodeList::getInstance()->broadcastToNodes(killPacket, NodeSet() << NodeType::AvatarMixer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) {
|
|
||||||
// first orbit horizontally
|
|
||||||
glm::quat orientation = getOrientation();
|
|
||||||
const float ANGULAR_SCALE = 0.5f;
|
|
||||||
glm::quat rotation = glm::angleAxis(glm::radians(- deltaX * ANGULAR_SCALE), orientation * IDENTITY_UP);
|
|
||||||
setPosition(position + rotation * (getPosition() - position));
|
|
||||||
orientation = rotation * orientation;
|
|
||||||
setOrientation(orientation);
|
|
||||||
|
|
||||||
// then vertically
|
|
||||||
float oldPitch = getHead()->getBasePitch();
|
|
||||||
getHead()->setBasePitch(oldPitch - deltaY * ANGULAR_SCALE);
|
|
||||||
rotation = glm::angleAxis(glm::radians((getHead()->getBasePitch() - oldPitch)), orientation * IDENTITY_RIGHT);
|
|
||||||
|
|
||||||
setPosition(position + rotation * (getPosition() - position));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MyAvatar::updateLookAtTargetAvatar() {
|
void MyAvatar::updateLookAtTargetAvatar() {
|
||||||
//
|
//
|
||||||
// Look at the avatar whose eyes are closest to the ray in direction of my avatar's head
|
// Look at the avatar whose eyes are closest to the ray in direction of my avatar's head
|
||||||
|
@ -536,6 +519,7 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
||||||
float smallestAngleTo = MIN_LOOKAT_ANGLE;
|
float smallestAngleTo = MIN_LOOKAT_ANGLE;
|
||||||
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) {
|
||||||
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
Avatar* avatar = static_cast<Avatar*>(avatarPointer.data());
|
||||||
|
avatar->setIsLookAtTarget(false);
|
||||||
if (!avatar->isMyAvatar()) {
|
if (!avatar->isMyAvatar()) {
|
||||||
float angleTo = glm::angle(getHead()->getFinalOrientation() * glm::vec3(0.0f, 0.0f, -1.0f),
|
float angleTo = glm::angle(getHead()->getFinalOrientation() * glm::vec3(0.0f, 0.0f, -1.0f),
|
||||||
glm::normalize(avatar->getHead()->getEyePosition() - getHead()->getEyePosition()));
|
glm::normalize(avatar->getHead()->getEyePosition() - getHead()->getEyePosition()));
|
||||||
|
@ -546,6 +530,9 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_lookAtTargetAvatar) {
|
||||||
|
static_cast<Avatar*>(_lookAtTargetAvatar.data())->setIsLookAtTarget(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::clearLookAtTargetAvatar() {
|
void MyAvatar::clearLookAtTargetAvatar() {
|
||||||
|
|
|
@ -77,8 +77,6 @@ public:
|
||||||
|
|
||||||
static void sendKillAvatar();
|
static void sendKillAvatar();
|
||||||
|
|
||||||
void orbit(const glm::vec3& position, int deltaX, int deltaY);
|
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
|
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
|
||||||
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
||||||
void updateLookAtTargetAvatar();
|
void updateLookAtTargetAvatar();
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
const float DEFAULT_KEYHOLE_RADIUS = 3.0f;
|
const float DEFAULT_KEYHOLE_RADIUS = 3.0f;
|
||||||
const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f;
|
const float DEFAULT_FIELD_OF_VIEW_DEGREES = 90.0f;
|
||||||
|
const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.f;
|
||||||
const float DEFAULT_ASPECT_RATIO = 16.f/9.f;
|
const float DEFAULT_ASPECT_RATIO = 16.f/9.f;
|
||||||
const float DEFAULT_NEAR_CLIP = 0.08f;
|
const float DEFAULT_NEAR_CLIP = 0.08f;
|
||||||
const float DEFAULT_FAR_CLIP = 50.0f * TREE_SCALE;
|
const float DEFAULT_FAR_CLIP = 50.0f * TREE_SCALE;
|
||||||
|
|
Loading…
Reference in a new issue