mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
merge from master
This commit is contained in:
commit
c9780338ae
142 changed files with 4004 additions and 2034 deletions
|
@ -70,7 +70,7 @@ void Agent::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointe
|
|||
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
|
||||
int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength;
|
||||
|
||||
std::unique_ptr<char> buffer = std::unique_ptr<char>(new char[piggyBackedSizeWithHeader]);
|
||||
auto buffer = std::unique_ptr<char[]>(new char[piggyBackedSizeWithHeader]);
|
||||
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader);
|
||||
|
||||
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, packet->getSenderSockAddr());
|
||||
|
@ -107,6 +107,7 @@ void Agent::handleAudioPacket(QSharedPointer<NLPacket> packet) {
|
|||
}
|
||||
|
||||
const QString AGENT_LOGGING_NAME = "agent";
|
||||
const int PING_INTERVAL = 1000;
|
||||
|
||||
void Agent::run() {
|
||||
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
|
||||
|
@ -118,6 +119,10 @@ void Agent::run() {
|
|||
<< NodeType::EntityServer
|
||||
);
|
||||
|
||||
_pingTimer = new QTimer(this);
|
||||
connect(_pingTimer, SIGNAL(timeout()), SLOT(sendPingRequests()));
|
||||
_pingTimer->start(PING_INTERVAL);
|
||||
|
||||
// figure out the URL for the script for this agent assignment
|
||||
QUrl scriptURL;
|
||||
if (_payload.isEmpty()) {
|
||||
|
@ -193,7 +198,27 @@ void Agent::run() {
|
|||
|
||||
void Agent::aboutToFinish() {
|
||||
_scriptEngine.stop();
|
||||
|
||||
|
||||
_pingTimer->stop();
|
||||
delete _pingTimer;
|
||||
|
||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(NULL);
|
||||
}
|
||||
|
||||
void Agent::sendPingRequests() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
nodeList->eachMatchingNode([](const SharedNodePointer& node)->bool {
|
||||
switch (node->getType()) {
|
||||
case NodeType::AvatarMixer:
|
||||
case NodeType::AudioMixer:
|
||||
case NodeType::EntityServer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}, [nodeList](const SharedNodePointer& node) {
|
||||
nodeList->sendPacket(nodeList->constructPingPacket(), *node);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -58,11 +58,13 @@ private slots:
|
|||
void handleAudioPacket(QSharedPointer<NLPacket> packet);
|
||||
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||
void sendPingRequests();
|
||||
|
||||
private:
|
||||
ScriptEngine _scriptEngine;
|
||||
EntityEditPacketSender _entityEditSender;
|
||||
EntityTreeHeadlessViewer _entityViewer;
|
||||
QTimer* _pingTimer;
|
||||
|
||||
MixedAudioStream _receivedAudioStream;
|
||||
float _lastReceivedAudioLoudness;
|
||||
|
|
|
@ -927,9 +927,9 @@ void AudioMixer::parseSettingsObject(const QJsonObject &settingsObject) {
|
|||
const QString USE_STDEV_FOR_DESIRED_CALC_JSON_KEY = "use_stdev_for_desired_calc";
|
||||
_streamSettings._useStDevForJitterCalc = audioBufferGroupObject[USE_STDEV_FOR_DESIRED_CALC_JSON_KEY].toBool();
|
||||
if (_streamSettings._useStDevForJitterCalc) {
|
||||
qDebug() << "Using Philip's stdev method for jitter calc if dynamic jitter buffers enabled";
|
||||
qDebug() << "Using stdev method for jitter calc if dynamic jitter buffers enabled";
|
||||
} else {
|
||||
qDebug() << "Using Fred's max-gap method for jitter calc if dynamic jitter buffers enabled";
|
||||
qDebug() << "Using max-gap method for jitter calc if dynamic jitter buffers enabled";
|
||||
}
|
||||
|
||||
const QString WINDOW_STARVE_THRESHOLD_JSON_KEY = "window_starve_threshold";
|
||||
|
|
|
@ -38,5 +38,19 @@ if (WIN32)
|
|||
find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES GLEW_DLL_PATH)
|
||||
|
||||
add_paths_to_fixup_libs(${GLEW_DLL_PATH})
|
||||
elseif (APPLE)
|
||||
else ()
|
||||
find_path(GLEW_INCLUDE_DIR GL/glew.h)
|
||||
find_library(GLEW_LIBRARY NAMES GLEW glew32 glew glew32s PATH_SUFFIXES lib64)
|
||||
|
||||
set(GLEW_INCLUDE_DIRS ${GLEW_INCLUDE_DIR})
|
||||
set(GLEW_LIBRARIES ${GLEW_LIBRARY})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(GLEW
|
||||
REQUIRED_VARS GLEW_INCLUDE_DIR GLEW_LIBRARY)
|
||||
|
||||
mark_as_advanced(GLEW_INCLUDE_DIR GLEW_LIBRARY)
|
||||
|
||||
endif ()
|
||||
|
||||
|
|
214
examples/controlPanel.js
Normal file
214
examples/controlPanel.js
Normal file
|
@ -0,0 +1,214 @@
|
|||
//
|
||||
// controlPanel.js
|
||||
// examples
|
||||
//
|
||||
// Created by Zander Otavka on 7/15/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Shows a few common controls in a FloatingUIPanel on right click.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include([
|
||||
"libraries/globals.js",
|
||||
"libraries/overlayManager.js",
|
||||
]);
|
||||
|
||||
var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg";
|
||||
var CLOSE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/close.svg";
|
||||
var MIC_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/mic-toggle.svg";
|
||||
var FACE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/face-toggle.svg";
|
||||
var ADDRESS_BAR_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/tools/address-bar-toggle.svg";
|
||||
|
||||
var panel = new FloatingUIPanel({
|
||||
anchorPosition: {
|
||||
bind: "myAvatar"
|
||||
},
|
||||
offsetPosition: { x: 0, y: 0.4, z: 1 }
|
||||
});
|
||||
|
||||
var background = new BillboardOverlay({
|
||||
url: BG_IMAGE_URL,
|
||||
dimensions: {
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false
|
||||
});
|
||||
panel.addChild(background);
|
||||
|
||||
var closeButton = new BillboardOverlay({
|
||||
url: CLOSE_IMAGE_URL,
|
||||
dimensions: {
|
||||
x: 0.15,
|
||||
y: 0.15,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false,
|
||||
offsetPosition: {
|
||||
x: -0.1,
|
||||
y: 0.1,
|
||||
z: -0.001
|
||||
}
|
||||
});
|
||||
closeButton.onClick = function(event) {
|
||||
panel.visible = false;
|
||||
};
|
||||
panel.addChild(closeButton);
|
||||
|
||||
var micMuteButton = new BillboardOverlay({
|
||||
url: MIC_IMAGE_URL,
|
||||
subImage: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 45,
|
||||
height: 45
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.15,
|
||||
y: 0.15,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false,
|
||||
offsetPosition: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
z: -0.001
|
||||
}
|
||||
});
|
||||
micMuteButton.onClick = function(event) {
|
||||
AudioDevice.toggleMute();
|
||||
};
|
||||
panel.addChild(micMuteButton);
|
||||
|
||||
var faceMuteButton = new BillboardOverlay({
|
||||
url: FACE_IMAGE_URL,
|
||||
subImage: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 45,
|
||||
height: 45
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.15,
|
||||
y: 0.15,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false,
|
||||
offsetPosition: {
|
||||
x: 0.1,
|
||||
y: -0.1,
|
||||
z: -0.001
|
||||
}
|
||||
});
|
||||
faceMuteButton.onClick = function(event) {
|
||||
FaceTracker.toggleMute();
|
||||
};
|
||||
panel.addChild(faceMuteButton);
|
||||
|
||||
var addressBarButton = new BillboardOverlay({
|
||||
url: ADDRESS_BAR_IMAGE_URL,
|
||||
subImage: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 45,
|
||||
height: 45
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.15,
|
||||
y: 0.15,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false,
|
||||
offsetPosition: {
|
||||
x: -0.1,
|
||||
y: -0.1,
|
||||
z: -0.001
|
||||
}
|
||||
});
|
||||
addressBarButton.onClick = function(event) {
|
||||
DialogsManager.toggleAddressBar();
|
||||
};
|
||||
panel.addChild(addressBarButton);
|
||||
|
||||
|
||||
function onMicMuteToggled() {
|
||||
var offset;
|
||||
if (AudioDevice.getMuted()) {
|
||||
offset = 45;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
micMuteButton.subImage = {
|
||||
x: offset,
|
||||
y: 0,
|
||||
width: 45,
|
||||
height: 45
|
||||
};
|
||||
}
|
||||
onMicMuteToggled();
|
||||
|
||||
function onFaceMuteToggled() {
|
||||
var offset;
|
||||
if (FaceTracker.getMuted()) {
|
||||
offset = 45;
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
faceMuteButton.subImage = {
|
||||
x: offset,
|
||||
y: 0,
|
||||
width: 45,
|
||||
height: 45
|
||||
};
|
||||
}
|
||||
onFaceMuteToggled();
|
||||
|
||||
var mouseDown = {};
|
||||
|
||||
function onMouseDown(event) {
|
||||
if (event.isLeftButton) {
|
||||
mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y });
|
||||
}
|
||||
if (event.isRightButton) {
|
||||
mouseDown.pos = { x: event.x, y: event.y };
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseUp(event) {
|
||||
if (event.isLeftButton) {
|
||||
var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y });
|
||||
if (overlay && overlay === mouseDown.overlay && overlay.onClick) {
|
||||
overlay.onClick(event);
|
||||
}
|
||||
}
|
||||
if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) {
|
||||
panel.setProperties({
|
||||
visible: !panel.visible,
|
||||
offsetRotation: {
|
||||
bind: "quat",
|
||||
value: Quat.multiply(MyAvatar.orientation, { x: 0, y: 1, z: 0, w: 0 })
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mouseDown = {};
|
||||
}
|
||||
|
||||
function onScriptEnd(event) {
|
||||
panel.destroy();
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(onMouseDown);
|
||||
Controller.mouseReleaseEvent.connect(onMouseUp);
|
||||
AudioDevice.muteToggled.connect(onMicMuteToggled);
|
||||
FaceTracker.muteToggled.connect(onFaceMuteToggled);
|
||||
Script.scriptEnding.connect(onScriptEnd);
|
169
examples/example/ui/floatingUIExample.js
Normal file
169
examples/example/ui/floatingUIExample.js
Normal file
|
@ -0,0 +1,169 @@
|
|||
//
|
||||
// floatingUI.js
|
||||
// examples/example/ui
|
||||
//
|
||||
// Created by Alexander Otavka
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include([
|
||||
"../../libraries/globals.js",
|
||||
"../../libraries/overlayManager.js",
|
||||
]);
|
||||
|
||||
var BG_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/card-bg.svg";
|
||||
var RED_DOT_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/red-dot.svg";
|
||||
var BLUE_SQUARE_IMAGE_URL = HIFI_PUBLIC_BUCKET + "images/blue-square.svg";
|
||||
|
||||
var mainPanel = new FloatingUIPanel({
|
||||
offsetRotation: {
|
||||
bind: "quat",
|
||||
value: { w: 1, x: 0, y: 0, z: 0 }
|
||||
},
|
||||
offsetPosition: { x: 0, y: 0.4, z: 1 }
|
||||
});
|
||||
|
||||
var bluePanel = mainPanel.addChild(new FloatingUIPanel ({
|
||||
offsetPosition: { x: 0.1, y: 0.1, z: -0.2 }
|
||||
}));
|
||||
|
||||
var mainPanelBackground = new BillboardOverlay({
|
||||
url: BG_IMAGE_URL,
|
||||
dimensions: {
|
||||
x: 0.5,
|
||||
y: 0.5,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false,
|
||||
offsetPosition: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0.001
|
||||
}
|
||||
});
|
||||
|
||||
var bluePanelBackground = mainPanelBackground.clone();
|
||||
bluePanelBackground.dimensions = {
|
||||
x: 0.3,
|
||||
y: 0.3
|
||||
};
|
||||
|
||||
mainPanel.addChild(mainPanelBackground);
|
||||
bluePanel.addChild(bluePanelBackground);
|
||||
|
||||
var redDot = mainPanel.addChild(new BillboardOverlay({
|
||||
url: RED_DOT_IMAGE_URL,
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false,
|
||||
offsetPosition: {
|
||||
x: -0.15,
|
||||
y: -0.15,
|
||||
z: 0
|
||||
}
|
||||
}));
|
||||
|
||||
var redDot2 = mainPanel.addChild(new BillboardOverlay({
|
||||
url: RED_DOT_IMAGE_URL,
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false,
|
||||
offsetPosition: {
|
||||
x: -0.155,
|
||||
y: 0.005,
|
||||
z: 0
|
||||
}
|
||||
}));
|
||||
|
||||
var blueSquare = bluePanel.addChild(new BillboardOverlay({
|
||||
url: BLUE_SQUARE_IMAGE_URL,
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false,
|
||||
offsetPosition: {
|
||||
x: 0.055,
|
||||
y: -0.055,
|
||||
z: 0
|
||||
}
|
||||
}));
|
||||
|
||||
var blueSquare2 = bluePanel.addChild(new BillboardOverlay({
|
||||
url: BLUE_SQUARE_IMAGE_URL,
|
||||
dimensions: {
|
||||
x: 0.1,
|
||||
y: 0.1,
|
||||
},
|
||||
isFacingAvatar: false,
|
||||
alpha: 1.0,
|
||||
ignoreRayIntersection: false,
|
||||
offsetPosition: {
|
||||
x: 0.055,
|
||||
y: 0.055,
|
||||
z: 0
|
||||
}
|
||||
}));
|
||||
|
||||
var blueSquare3 = blueSquare2.clone();
|
||||
blueSquare3.offsetPosition = {
|
||||
x: -0.055,
|
||||
y: 0.055,
|
||||
z: 0
|
||||
};
|
||||
|
||||
|
||||
var mouseDown = {};
|
||||
|
||||
function onMouseDown(event) {
|
||||
if (event.isLeftButton) {
|
||||
mouseDown.overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y });
|
||||
}
|
||||
if (event.isRightButton) {
|
||||
mouseDown.pos = { x: event.x, y: event.y };
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseUp(event) {
|
||||
if (event.isLeftButton) {
|
||||
var overlay = OverlayManager.findAtPoint({ x: event.x, y: event.y });
|
||||
if (overlay === mouseDown.overlay) {
|
||||
if (overlay.attachedPanel === bluePanel) {
|
||||
overlay.destroy();
|
||||
} else if (overlay) {
|
||||
var oldPos = overlay.offsetPosition;
|
||||
var newPos = {
|
||||
x: Number(oldPos.x),
|
||||
y: Number(oldPos.y),
|
||||
z: Number(oldPos.z) + 0.1
|
||||
};
|
||||
overlay.offsetPosition = newPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.isRightButton && Vec3.distance(mouseDown.pos, { x: event.x, y: event.y }) < 5) {
|
||||
mainPanel.visible = !mainPanel.visible;
|
||||
}
|
||||
}
|
||||
|
||||
function onScriptEnd() {
|
||||
mainPanel.destroy();
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(onMouseDown);
|
||||
Controller.mouseReleaseEvent.connect(onMouseUp);
|
||||
Script.scriptEnding.connect(onScriptEnd);
|
|
@ -9,7 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var MOVE_DISTANCE = 10.0;
|
||||
var MOVE_DISTANCE = 2.0;
|
||||
var PITCH_INCREMENT = 0.5; // degrees
|
||||
var pitchChange = 0; // degrees
|
||||
var YAW_INCREMENT = 0.5; // degrees
|
||||
|
|
|
@ -23,7 +23,7 @@ var RAD_TO_DEG = 180.0 / PI;
|
|||
var AZIMUTH_RATE = 90.0;
|
||||
var ALTITUDE_RATE = 200.0;
|
||||
var RADIUS_RATE = 1.0 / 100.0;
|
||||
var PAN_RATE = 50.0;
|
||||
var PAN_RATE = 250.0;
|
||||
|
||||
var Y_AXIS = {
|
||||
x: 0,
|
||||
|
@ -139,7 +139,7 @@ function handlePanMode(dx, dy) {
|
|||
var right = Quat.getRight(Camera.getOrientation());
|
||||
var distance = Vec3.length(vector);
|
||||
|
||||
var dv = Vec3.sum(Vec3.multiply(up, -distance * dy / PAN_RATE), Vec3.multiply(right, distance * dx / PAN_RATE));
|
||||
var dv = Vec3.sum(Vec3.multiply(up, distance * dy / PAN_RATE), Vec3.multiply(right, -distance * dx / PAN_RATE));
|
||||
|
||||
center = Vec3.sum(center, dv);
|
||||
position = Vec3.sum(position, dv);
|
||||
|
|
455
examples/libraries/overlayManager.js
Normal file
455
examples/libraries/overlayManager.js
Normal file
|
@ -0,0 +1,455 @@
|
|||
//
|
||||
// overlayManager.js
|
||||
// examples/libraries
|
||||
//
|
||||
// Created by Zander Otavka on 7/24/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Manage overlays with object oriented goodness, instead of ugly `Overlays.h` methods.
|
||||
// Instead of:
|
||||
//
|
||||
// var billboard = Overlays.addOverlay("billboard", { visible: false });
|
||||
// ...
|
||||
// Overlays.editOverlay(billboard, { visible: true });
|
||||
// ...
|
||||
// Overlays.deleteOverlay(billboard);
|
||||
//
|
||||
// You can now do:
|
||||
//
|
||||
// var billboard = new BillboardOverlay({ visible: false });
|
||||
// ...
|
||||
// billboard.visible = true;
|
||||
// ...
|
||||
// billboard.destroy();
|
||||
//
|
||||
// See more on usage below.
|
||||
//
|
||||
// Note that including this file will delete Overlays from the global scope. All the
|
||||
// functionality of Overlays is represented here, just better. If you try to use Overlays in
|
||||
// tandem, there may be performance problems or nasty surprises.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
(function() {
|
||||
// Delete `Overlays` from the global scope.
|
||||
var Overlays = this.Overlays;
|
||||
delete this.Overlays;
|
||||
|
||||
var overlays = {};
|
||||
var panels = {};
|
||||
|
||||
var overlayTypes;
|
||||
var Overlay, Overlay2D, Base3DOverlay, Planar3DOverlay, Volume3DOverlay;
|
||||
|
||||
|
||||
//
|
||||
// Create a new JavaScript object for an overlay of given ID.
|
||||
//
|
||||
function makeOverlayFromId(id) {
|
||||
var type = Overlays.getOverlayType(id);
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
var overlay = new overlayTypes[type]();
|
||||
overlay._id = id;
|
||||
var panelID = Overlays.getAttachedPanel(id)
|
||||
if (panelID && panelID in panels) {
|
||||
panels[panelID].addChild(overlay);
|
||||
}
|
||||
overlays[id] = overlay;
|
||||
return overlay;
|
||||
}
|
||||
|
||||
//
|
||||
// Get or create an overlay object from the id.
|
||||
//
|
||||
// @param knownOverlaysOnly (Optional: Boolean)
|
||||
// If true, a new object will not be created.
|
||||
// @param searchList (Optional: Object)
|
||||
// Map of overlay id's and overlay objects. Can be generated with
|
||||
// `OverlayManager.makeSearchList`.
|
||||
//
|
||||
function findOverlay(id, knownOverlaysOnly, searchList) {
|
||||
if (id > 0) {
|
||||
knownOverlaysOnly = Boolean(knownOverlaysOnly) || Boolean(searchList);
|
||||
searchList = searchList || overlays;
|
||||
var foundOverlay = searchList[id];
|
||||
if (foundOverlay) {
|
||||
return foundOverlay;
|
||||
}
|
||||
if (!knownOverlaysOnly) {
|
||||
return makeOverlayFromId(id);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Perform global scoped operations on overlays, such as finding by ray intersection.
|
||||
//
|
||||
OverlayManager = {
|
||||
findOnRay: function(pickRay, knownOverlaysOnly, searchList) {
|
||||
var rayPickResult = Overlays.findRayIntersection(pickRay);
|
||||
print("raypick " + rayPickResult.overlayID);
|
||||
if (rayPickResult.intersects) {
|
||||
return findOverlay(rayPickResult.overlayID, knownOverlaysOnly, searchList);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
findAtPoint: function(point, knownOverlaysOnly, searchList) {
|
||||
var foundID = Overlays.getOverlayAtPoint(point);
|
||||
print("at point " + foundID);
|
||||
if (foundID) {
|
||||
return findOverlay(foundID, knownOverlaysOnly, searchList);
|
||||
} else {
|
||||
var pickRay = Camera.computePickRay(point.x, point.y);
|
||||
return OverlayManager.findOnRay(pickRay, knownOverlaysOnly, searchList);
|
||||
}
|
||||
},
|
||||
makeSearchList: function(overlayArray) {
|
||||
var searchList = {};
|
||||
overlayArray.forEach(function(overlay){
|
||||
searchList[overlay._id] = overlay;
|
||||
});
|
||||
return searchList;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Object oriented abstraction layer for overlays.
|
||||
//
|
||||
// Usage:
|
||||
// // Create an overlay
|
||||
// var billboard = new BillboardOverlay({
|
||||
// visible: true,
|
||||
// isFacingAvatar: true,
|
||||
// ignoreRayIntersections: false
|
||||
// });
|
||||
//
|
||||
// // Get a property
|
||||
// var isVisible = billboard.visible;
|
||||
//
|
||||
// // Set a single property
|
||||
// billboard.position = { x: 1, y: 3, z: 2 };
|
||||
//
|
||||
// // Set multiple properties at the same time
|
||||
// billboard.setProperties({
|
||||
// url: "http://images.com/overlayImage.jpg",
|
||||
// dimensions: { x: 2, y: 2 }
|
||||
// });
|
||||
//
|
||||
// // Clone an overlay
|
||||
// var clonedBillboard = billboard.clone();
|
||||
//
|
||||
// // Remove an overlay from the world
|
||||
// billboard.destroy();
|
||||
//
|
||||
// // Remember, there is a poor orphaned JavaScript object left behind. You should
|
||||
// // remove any references to it so you don't accidentally try to modify an overlay that
|
||||
// // isn't there.
|
||||
// billboard = undefined;
|
||||
//
|
||||
(function() {
|
||||
var ABSTRACT = null;
|
||||
overlayTypes = {};
|
||||
|
||||
function generateOverlayClass(superclass, type, properties) {
|
||||
var that;
|
||||
if (type == ABSTRACT) {
|
||||
that = function(type, params) {
|
||||
superclass.call(this, type, params);
|
||||
};
|
||||
} else {
|
||||
that = function(params) {
|
||||
superclass.call(this, type, params);
|
||||
};
|
||||
overlayTypes[type] = that;
|
||||
}
|
||||
|
||||
that.prototype = new superclass();
|
||||
that.prototype.constructor = that;
|
||||
|
||||
properties.forEach(function(prop) {
|
||||
Object.defineProperty(that.prototype, prop, {
|
||||
get: function() {
|
||||
return Overlays.getProperty(this._id, prop);
|
||||
},
|
||||
set: function(newValue) {
|
||||
var keyValuePair = {};
|
||||
keyValuePair[prop] = newValue;
|
||||
this.setProperties(keyValuePair);
|
||||
},
|
||||
configurable: false
|
||||
});
|
||||
});
|
||||
|
||||
return that;
|
||||
}
|
||||
|
||||
// Supports multiple inheritance of properties. Just `concat` them onto the end of the
|
||||
// properties list.
|
||||
var PANEL_ATTACHABLE_FIELDS = ["offsetPosition", "facingRotation"];
|
||||
|
||||
Overlay = (function() {
|
||||
var that = function(type, params) {
|
||||
if (type && params) {
|
||||
this._id = Overlays.addOverlay(type, params);
|
||||
overlays[this._id] = this;
|
||||
} else {
|
||||
this._id = 0;
|
||||
}
|
||||
this._attachedPanelPointer = null;
|
||||
};
|
||||
|
||||
that.prototype.constructor = that;
|
||||
|
||||
Object.defineProperty(that.prototype, "isLoaded", {
|
||||
get: function() {
|
||||
return Overlays.isLoaded(this._id);
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(that.prototype, "attachedPanel", {
|
||||
get: function() {
|
||||
return this._attachedPanelPointer;
|
||||
}
|
||||
});
|
||||
|
||||
that.prototype.getTextSize = function(text) {
|
||||
return Overlays.textSize(this._id, text);
|
||||
};
|
||||
|
||||
that.prototype.setProperties = function(properties) {
|
||||
Overlays.editOverlay(this._id, properties);
|
||||
};
|
||||
|
||||
that.prototype.clone = function() {
|
||||
return makeOverlayFromId(Overlays.cloneOverlay(this._id));
|
||||
};
|
||||
|
||||
that.prototype.destroy = function() {
|
||||
Overlays.deleteOverlay(this._id);
|
||||
};
|
||||
|
||||
return generateOverlayClass(that, ABSTRACT, [
|
||||
"alpha", "glowLevel", "pulseMax", "pulseMin", "pulsePeriod", "glowLevelPulse",
|
||||
"alphaPulse", "colorPulse", "visible", "anchor"
|
||||
]);
|
||||
})();
|
||||
|
||||
Overlay2D = generateOverlayClass(Overlay, ABSTRACT, [
|
||||
"bounds", "x", "y", "width", "height"
|
||||
]);
|
||||
|
||||
Base3DOverlay = generateOverlayClass(Overlay, ABSTRACT, [
|
||||
"position", "lineWidth", "rotation", "isSolid", "isFilled", "isWire", "isDashedLine",
|
||||
"ignoreRayIntersection", "drawInFront", "drawOnHUD"
|
||||
]);
|
||||
|
||||
Planar3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
|
||||
"dimensions"
|
||||
]);
|
||||
|
||||
Volume3DOverlay = generateOverlayClass(Base3DOverlay, ABSTRACT, [
|
||||
"dimensions"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Overlay2D, "image", [
|
||||
"subImage", "imageURL"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Overlay2D, "text", [
|
||||
"font", "text", "backgroundColor", "backgroundAlpha", "leftMargin", "topMargin"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Planar3DOverlay, "text3d", [
|
||||
"text", "backgroundColor", "backgroundAlpha", "lineHeight", "leftMargin", "topMargin",
|
||||
"rightMargin", "bottomMargin", "isFacingAvatar"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Volume3DOverlay, "cube", [
|
||||
"borderSize"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Volume3DOverlay, "sphere", [
|
||||
]);
|
||||
|
||||
generateOverlayClass(Planar3DOverlay, "circle3d", [
|
||||
"startAt", "endAt", "outerRadius", "innerRadius", "hasTickMarks",
|
||||
"majorTickMarksAngle", "minorTickMarksAngle", "majorTickMarksLength",
|
||||
"minorTickMarksLength", "majorTickMarksColor", "minorTickMarksColor"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Planar3DOverlay, "rectangle3d", [
|
||||
]);
|
||||
|
||||
generateOverlayClass(Base3DOverlay, "line3d", [
|
||||
"start", "end"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Planar3DOverlay, "grid", [
|
||||
"minorGridWidth", "majorGridEvery"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Volume3DOverlay, "localmodels", [
|
||||
]);
|
||||
|
||||
generateOverlayClass(Volume3DOverlay, "model", [
|
||||
"url", "dimensions", "textures"
|
||||
]);
|
||||
|
||||
generateOverlayClass(Planar3DOverlay, "billboard", [
|
||||
"url", "subImage", "isFacingAvatar"
|
||||
].concat(PANEL_ATTACHABLE_FIELDS));
|
||||
})();
|
||||
|
||||
ImageOverlay = overlayTypes["image"];
|
||||
TextOverlay = overlayTypes["text"];
|
||||
Text3DOverlay = overlayTypes["text3d"];
|
||||
Cube3DOverlay = overlayTypes["cube"];
|
||||
Sphere3DOverlay = overlayTypes["sphere"];
|
||||
Circle3DOverlay = overlayTypes["circle3d"];
|
||||
Rectangle3DOverlay = overlayTypes["rectangle3d"];
|
||||
Line3DOverlay = overlayTypes["line3d"];
|
||||
Grid3DOverlay = overlayTypes["grid"];
|
||||
LocalModelsOverlay = overlayTypes["localmodels"];
|
||||
ModelOverlay = overlayTypes["model"];
|
||||
BillboardOverlay = overlayTypes["billboard"];
|
||||
|
||||
|
||||
//
|
||||
// Object oriented abstraction layer for panels.
|
||||
//
|
||||
FloatingUIPanel = (function() {
|
||||
var that = function(params) {
|
||||
this._id = Overlays.addPanel(params);
|
||||
this._children = [];
|
||||
this._visible = Boolean(params.visible);
|
||||
panels[this._id] = this;
|
||||
this._attachedPanelPointer = null;
|
||||
};
|
||||
|
||||
that.prototype.constructor = that;
|
||||
|
||||
var FIELDS = ["offsetPosition", "offsetRotation", "facingRotation"];
|
||||
FIELDS.forEach(function(prop) {
|
||||
Object.defineProperty(that.prototype, prop, {
|
||||
get: function() {
|
||||
return Overlays.getPanelProperty(this._id, prop);
|
||||
},
|
||||
set: function(newValue) {
|
||||
var keyValuePair = {};
|
||||
keyValuePair[prop] = newValue;
|
||||
this.setProperties(keyValuePair);
|
||||
},
|
||||
configurable: false
|
||||
});
|
||||
});
|
||||
|
||||
var PSEUDO_FIELDS = [];
|
||||
|
||||
PSEUDO_FIELDS.push("children");
|
||||
Object.defineProperty(that.prototype, "children", {
|
||||
get: function() {
|
||||
return this._children.slice();
|
||||
}
|
||||
});
|
||||
|
||||
PSEUDO_FIELDS.push("visible");
|
||||
Object.defineProperty(that.prototype, "visible", {
|
||||
get: function() {
|
||||
return this._visible;
|
||||
},
|
||||
set: function(visible) {
|
||||
this._visible = visible;
|
||||
this._children.forEach(function(child) {
|
||||
child.visible = visible;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
that.prototype.addChild = function(child) {
|
||||
if (child instanceof Overlay) {
|
||||
Overlays.setAttachedPanel(child._id, this._id);
|
||||
} else if (child instanceof FloatingUIPanel) {
|
||||
child.setProperties({
|
||||
anchorPosition: {
|
||||
bind: "panel",
|
||||
value: this._id
|
||||
},
|
||||
offsetRotation: {
|
||||
bind: "panel",
|
||||
value: this._id
|
||||
}
|
||||
});
|
||||
}
|
||||
child._attachedPanelPointer = this;
|
||||
child.visible = this.visible;
|
||||
this._children.push(child);
|
||||
return child;
|
||||
};
|
||||
|
||||
that.prototype.removeChild = function(child) {
|
||||
var i = this._children.indexOf(child);
|
||||
if (i >= 0) {
|
||||
if (child instanceof Overlay) {
|
||||
Overlays.setAttachedPanel(child._id, 0);
|
||||
} else if (child instanceof FloatingUIPanel) {
|
||||
child.setProperties({
|
||||
anchorPosition: {
|
||||
bind: "myAvatar"
|
||||
},
|
||||
offsetRotation: {
|
||||
bind: "myAvatar"
|
||||
}
|
||||
});
|
||||
}
|
||||
child._attachedPanelPointer = null;
|
||||
this._children.splice(i, 1);
|
||||
}
|
||||
};
|
||||
|
||||
that.prototype.setProperties = function(properties) {
|
||||
for (var i in PSEUDO_FIELDS) {
|
||||
if (properties[PSEUDO_FIELDS[i]] !== undefined) {
|
||||
this[PSEUDO_FIELDS[i]] = properties[PSEUDO_FIELDS[i]];
|
||||
}
|
||||
}
|
||||
Overlays.editPanel(this._id, properties);
|
||||
};
|
||||
|
||||
that.prototype.destroy = function() {
|
||||
Overlays.deletePanel(this._id);
|
||||
};
|
||||
|
||||
return that;
|
||||
})();
|
||||
|
||||
|
||||
function onOverlayDeleted(id) {
|
||||
if (id in overlays) {
|
||||
if (overlays[id]._attachedPanelPointer) {
|
||||
overlays[id]._attachedPanelPointer.removeChild(overlays[id]);
|
||||
}
|
||||
delete overlays[id];
|
||||
}
|
||||
}
|
||||
|
||||
function onPanelDeleted(id) {
|
||||
if (id in panels) {
|
||||
panels[id]._children.forEach(function(child) {
|
||||
print(JSON.stringify(child.destroy));
|
||||
child.destroy();
|
||||
});
|
||||
delete panels[id];
|
||||
}
|
||||
}
|
||||
|
||||
Overlays.overlayDeleted.connect(onOverlayDeleted);
|
||||
Overlays.panelDeleted.connect(onPanelDeleted);
|
||||
})();
|
|
@ -1,7 +1,17 @@
|
|||
/**
|
||||
* OverlayGroup provides a way to create composite overlays and control their
|
||||
* position relative to a settable rootPosition and rootRotation.
|
||||
*/
|
||||
//
|
||||
// overlayUtils.js
|
||||
// examples/libraries
|
||||
//
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// DEPRECATION WARNING: Will be deprecated soon in favor of FloatingUIPanel.
|
||||
//
|
||||
// OverlayGroup provides a way to create composite overlays and control their
|
||||
// position relative to a settable rootPosition and rootRotation.
|
||||
//
|
||||
OverlayGroup = function(opts) {
|
||||
var that = {};
|
||||
|
||||
|
@ -59,6 +69,6 @@ OverlayGroup = function(opts) {
|
|||
}
|
||||
overlays = {};
|
||||
}
|
||||
|
||||
|
||||
return that;
|
||||
};
|
||||
|
|
|
@ -50,7 +50,7 @@ void IceServer::processDatagrams() {
|
|||
while (_serverSocket.hasPendingDatagrams()) {
|
||||
// setup a buffer to read the packet into
|
||||
int packetSizeWithHeader = _serverSocket.pendingDatagramSize();
|
||||
std::unique_ptr<char> buffer = std::unique_ptr<char>(new char[packetSizeWithHeader]);
|
||||
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
|
||||
|
||||
_serverSocket.readDatagram(buffer.get(), packetSizeWithHeader,
|
||||
sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer());
|
||||
|
|
|
@ -218,33 +218,13 @@ else (APPLE)
|
|||
"${PROJECT_SOURCE_DIR}/resources"
|
||||
$<TARGET_FILE_DIR:${TARGET_NAME}>/resources
|
||||
)
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
if (${OPENGL_INCLUDE_DIR})
|
||||
include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}")
|
||||
|
||||
|
||||
# link target to external libraries
|
||||
if (WIN32)
|
||||
add_dependency_external_projects(glew)
|
||||
find_package(GLEW REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib)
|
||||
|
||||
if (USE_NSIGHT)
|
||||
# try to find the Nsight package and add it to the build if we find it
|
||||
find_package(NSIGHT)
|
||||
if (NSIGHT_FOUND)
|
||||
include_directories(${NSIGHT_INCLUDE_DIRS})
|
||||
add_definitions(-DNSIGHT_FOUND)
|
||||
target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
# target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib)
|
||||
target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib)
|
||||
else (WIN32)
|
||||
# Nothing else required on linux apparently
|
||||
endif()
|
||||
endif (APPLE)
|
||||
|
||||
|
|
|
@ -772,8 +772,9 @@ void Application::initializeGL() {
|
|||
}
|
||||
#endif
|
||||
|
||||
// Where the gpuContext is created and where the TRUE Backend is created and assigned
|
||||
_gpuContext = std::make_shared<gpu::Context>(new gpu::GLBackend());
|
||||
// Where the gpuContext is initialized and where the TRUE Backend is created and assigned
|
||||
gpu::Context::init<gpu::GLBackend>();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
|
||||
initDisplay();
|
||||
qCDebug(interfaceapp, "Initialized Display.");
|
||||
|
@ -3757,10 +3758,13 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
|
||||
scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());
|
||||
qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);
|
||||
|
||||
scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue);
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "avatar/MyAvatar.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
#include "scripting/ControllerScriptingInterface.h"
|
||||
#include "scripting/DialogsManagerScriptingInterface.h"
|
||||
#include "scripting/WebWindowClass.h"
|
||||
#include "ui/AudioStatsDialog.h"
|
||||
#include "ui/BandwidthDialog.h"
|
||||
|
@ -69,6 +70,7 @@
|
|||
#include "UndoStackScriptingInterface.h"
|
||||
|
||||
#include "gpu/Context.h"
|
||||
|
||||
#include "render/Engine.h"
|
||||
|
||||
class QGLWidget;
|
||||
|
@ -643,6 +645,8 @@ private:
|
|||
ApplicationOverlay _applicationOverlay;
|
||||
ApplicationCompositor _compositor;
|
||||
int _numFramesSinceLastResize = 0;
|
||||
|
||||
DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_GLCanvas_h
|
||||
|
||||
#include <QDebug>
|
||||
#include <gpu/GPUConfig.h>
|
||||
#include <QGLWidget>
|
||||
#include <QTimer>
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
#include <mutex>
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <gpu/GPUConfig.h>
|
||||
#include <gpu/Batch.h>
|
||||
#include <gpu/Context.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <DependencyManager.h>
|
||||
|
@ -208,8 +206,6 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
|||
batch._glUniform1f(_timeSlot, secs);
|
||||
geometryCache->renderUnitCube(batch);
|
||||
|
||||
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
|
||||
|
||||
static const size_t VERTEX_STRIDE = sizeof(StarVertex);
|
||||
size_t offset = offsetof(StarVertex, position);
|
||||
gpu::BufferView posView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement);
|
||||
|
@ -218,10 +214,11 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
|||
|
||||
// Render the stars
|
||||
batch.setPipeline(_starsPipeline);
|
||||
|
||||
batch.setInputFormat(streamFormat);
|
||||
batch.setInputBuffer(VERTICES_SLOT, posView);
|
||||
batch.setInputBuffer(COLOR_SLOT, colView);
|
||||
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
|
||||
|
||||
|
||||
renderArgs->_context->render(batch);
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "Util.h"
|
||||
#include "world.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "EntityRig.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -75,9 +76,9 @@ namespace render {
|
|||
}
|
||||
}
|
||||
|
||||
Avatar::Avatar() :
|
||||
Avatar::Avatar(RigPointer rig) :
|
||||
AvatarData(),
|
||||
_skeletonModel(this),
|
||||
_skeletonModel(this, nullptr, rig),
|
||||
_skeletonOffset(0.0f),
|
||||
_bodyYawDelta(0.0f),
|
||||
_positionDeltaAccumulator(0.0f),
|
||||
|
@ -430,15 +431,17 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: re-implement these when we have more detailed avatar collision shapes
|
||||
bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes);
|
||||
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
|
||||
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
|
||||
if (renderSkeleton) {
|
||||
_skeletonModel.renderJointCollisionShapes(0.7f);
|
||||
}
|
||||
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
|
||||
if (renderHead && shouldRenderHead(renderArgs)) {
|
||||
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
|
||||
}
|
||||
*/
|
||||
|
||||
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
|
||||
if (renderBounding && shouldRenderHead(renderArgs)) {
|
||||
_skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f);
|
||||
}
|
||||
|
@ -793,33 +796,6 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co
|
|||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
|
||||
}
|
||||
|
||||
bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const {
|
||||
bool hit = _skeletonModel.findRayIntersection(intersection);
|
||||
hit = getHead()->getFaceModel().findRayIntersection(intersection) || hit;
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions) {
|
||||
return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
|
||||
// TODO: Andrew to fix: Temporarily disabling collisions against the head
|
||||
//return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
|
||||
}
|
||||
|
||||
bool Avatar::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) {
|
||||
return _skeletonModel.findPlaneCollisions(plane, collisions) ||
|
||||
getHead()->getFaceModel().findPlaneCollisions(plane, collisions);
|
||||
}
|
||||
|
||||
bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions) {
|
||||
// TODO: Andrew to fix: also collide against _skeleton
|
||||
//bool collided = _skeletonModel.findCollisions(shapes, collisions);
|
||||
|
||||
Model& headModel = getHead()->getFaceModel();
|
||||
//collided = headModel.findCollisions(shapes, collisions) || collided;
|
||||
bool collided = headModel.findCollisions(shapes, collisions);
|
||||
return collided;
|
||||
}
|
||||
|
||||
void Avatar::setSkeletonOffset(const glm::vec3& offset) {
|
||||
const float MAX_OFFSET_LENGTH = _scale * 0.5f;
|
||||
float offsetLength = glm::length(offset);
|
||||
|
@ -843,7 +819,7 @@ QVector<glm::quat> Avatar::getJointRotations() const {
|
|||
}
|
||||
QVector<glm::quat> jointRotations(_skeletonModel.getJointStateCount());
|
||||
for (int i = 0; i < _skeletonModel.getJointStateCount(); ++i) {
|
||||
_skeletonModel.getJointState(i, jointRotations[i]);
|
||||
_skeletonModel.getJointRotation(i, jointRotations[i]);
|
||||
}
|
||||
return jointRotations;
|
||||
}
|
||||
|
@ -853,7 +829,7 @@ glm::quat Avatar::getJointRotation(int index) const {
|
|||
return AvatarData::getJointRotation(index);
|
||||
}
|
||||
glm::quat rotation;
|
||||
_skeletonModel.getJointState(index, rotation);
|
||||
_skeletonModel.getJointRotation(index, rotation);
|
||||
return rotation;
|
||||
}
|
||||
|
||||
|
@ -977,7 +953,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
if (_unusedAttachments.size() > 0) {
|
||||
model = _unusedAttachments.takeFirst();
|
||||
} else {
|
||||
model = new Model(this);
|
||||
model = new Model(std::make_shared<EntityRig>(), this);
|
||||
}
|
||||
model->init();
|
||||
_attachmentModels.append(model);
|
||||
|
@ -1139,9 +1115,8 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
|
|||
|
||||
// virtual
|
||||
void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
|
||||
shapeInfo.setCapsuleY(capsule.getRadius(), capsule.getHalfHeight());
|
||||
shapeInfo.setOffset(_skeletonModel.getBoundingShapeOffset());
|
||||
shapeInfo.setCapsuleY(_skeletonModel.getBoundingCapsuleRadius(), 0.5f * _skeletonModel.getBoundingCapsuleHeight());
|
||||
shapeInfo.setOffset(_skeletonModel.getBoundingCapsuleOffset());
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
|
|
@ -72,7 +72,7 @@ class Avatar : public AvatarData {
|
|||
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
|
||||
|
||||
public:
|
||||
Avatar();
|
||||
Avatar(RigPointer rig = nullptr);
|
||||
~Avatar();
|
||||
|
||||
typedef render::Payload<AvatarData> Payload;
|
||||
|
@ -110,26 +110,6 @@ public:
|
|||
/// Returns the distance to use as a LOD parameter.
|
||||
float getLODDistance() const;
|
||||
|
||||
bool findRayIntersection(RayIntersectionInfo& intersection) const;
|
||||
|
||||
/// \param shapes list of shapes to collide against avatar
|
||||
/// \param collisions list to store collision results
|
||||
/// \return true if at least one shape collided with avatar
|
||||
bool findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions);
|
||||
|
||||
/// Checks for penetration between the a sphere and the avatar's models.
|
||||
/// \param penetratorCenter the center of the penetration test sphere
|
||||
/// \param penetratorRadius the radius of the penetration test sphere
|
||||
/// \param collisions[out] a list to which collisions get appended
|
||||
/// \return whether or not the sphere penetrated
|
||||
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions);
|
||||
|
||||
/// Checks for penetration between the described plane and the avatar.
|
||||
/// \param plane the penetration plane
|
||||
/// \param collisions[out] a list to which collisions get appended
|
||||
/// \return whether or not the plane penetrated
|
||||
bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
|
||||
|
||||
virtual bool isMyAvatar() const { return false; }
|
||||
|
||||
virtual QVector<glm::quat> getJointRotations() const;
|
||||
|
@ -186,9 +166,10 @@ public:
|
|||
|
||||
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
|
||||
|
||||
friend class AvatarManager;
|
||||
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
|
||||
AvatarMotionState* getMotionState() { return _motionState; }
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
|
||||
|
||||
protected:
|
||||
|
@ -218,7 +199,7 @@ protected:
|
|||
glm::vec3 _worldUpDirection;
|
||||
float _stringLength;
|
||||
bool _moving; ///< set when position is changing
|
||||
|
||||
|
||||
// protected methods...
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
|
@ -243,7 +224,7 @@ protected:
|
|||
virtual void updateJointMappings();
|
||||
|
||||
render::ItemID _renderItemID;
|
||||
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
NetworkTexturePointer _billboardTexture;
|
||||
|
@ -251,9 +232,9 @@ private:
|
|||
bool _isLookAtTarget;
|
||||
|
||||
void renderBillboard(RenderArgs* renderArgs);
|
||||
|
||||
|
||||
float getBillboardSize() const;
|
||||
|
||||
|
||||
static int _jointConesID;
|
||||
|
||||
int _voiceSphereID;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "Menu.h"
|
||||
#include "MyAvatar.h"
|
||||
#include "SceneScriptingInterface.h"
|
||||
#include "AvatarRig.h"
|
||||
|
||||
// 70 times per second - target is 60hz, but this helps account for any small deviations
|
||||
// in the update loop
|
||||
|
@ -65,7 +66,7 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
{
|
||||
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
|
||||
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
|
||||
_myAvatar = std::make_shared<MyAvatar>();
|
||||
_myAvatar = std::make_shared<MyAvatar>(std::make_shared<AvatarRig>());
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket");
|
||||
|
@ -160,7 +161,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
||||
return AvatarSharedPointer(std::make_shared<Avatar>());
|
||||
return AvatarSharedPointer(std::make_shared<Avatar>(std::make_shared<AvatarRig>()));
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -178,11 +179,11 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
|
|||
// protected
|
||||
void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
|
||||
auto rawPointer = std::static_pointer_cast<Avatar>(avatar);
|
||||
AvatarMotionState* motionState= rawPointer->_motionState;
|
||||
AvatarMotionState* motionState = rawPointer->getMotionState();
|
||||
if (motionState) {
|
||||
// clean up physics stuff
|
||||
motionState->clearObjectBackPointer();
|
||||
rawPointer->_motionState = nullptr;
|
||||
rawPointer->setMotionState(nullptr);
|
||||
_avatarMotionStates.remove(motionState);
|
||||
_motionStatesToAdd.remove(motionState);
|
||||
_motionStatesToDelete.push_back(motionState);
|
||||
|
@ -306,7 +307,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) {
|
|||
AvatarHash::iterator avatarItr = _avatarHash.find(id);
|
||||
if (avatarItr != _avatarHash.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarItr.value());
|
||||
AvatarMotionState* motionState = avatar->_motionState;
|
||||
AvatarMotionState* motionState = avatar->getMotionState();
|
||||
if (motionState) {
|
||||
motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE);
|
||||
} else {
|
||||
|
@ -315,7 +316,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) {
|
|||
btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||
if (shape) {
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape);
|
||||
avatar->_motionState = motionState;
|
||||
avatar->setMotionState(motionState);
|
||||
_motionStatesToAdd.insert(motionState);
|
||||
_avatarMotionStates.insert(motionState);
|
||||
}
|
||||
|
|
|
@ -16,13 +16,16 @@
|
|||
#include "Head.h"
|
||||
#include "Menu.h"
|
||||
|
||||
FaceModel::FaceModel(Head* owningHead) :
|
||||
FaceModel::FaceModel(Head* owningHead, RigPointer rig) :
|
||||
Model(rig, nullptr),
|
||||
_owningHead(owningHead)
|
||||
{
|
||||
assert(_rig);
|
||||
}
|
||||
|
||||
void FaceModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
updateGeometry();
|
||||
|
||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar);
|
||||
glm::vec3 neckPosition;
|
||||
if (!owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) {
|
||||
|
@ -35,67 +38,73 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
}
|
||||
setRotation(neckParentRotation);
|
||||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale());
|
||||
|
||||
|
||||
setPupilDilation(_owningHead->getPupilDilation());
|
||||
setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients());
|
||||
|
||||
|
||||
// FIXME - this is very expensive, we shouldn't do it if we don't have to
|
||||
//invalidCalculatedMeshBoxes();
|
||||
|
||||
if (isActive()) {
|
||||
setOffset(-_geometry->getFBXGeometry().neckPivot);
|
||||
|
||||
for (int i = 0; i < _rig->getJointStateCount(); i++) {
|
||||
maybeUpdateNeckAndEyeRotation(i);
|
||||
}
|
||||
|
||||
Model::simulateInternal(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index) {
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
|
||||
glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
|
||||
glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||
glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame());
|
||||
glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(),
|
||||
_owningHead->getTorsoTwist(),
|
||||
_owningHead->getFinalLeanSideways()));
|
||||
pitchYawRoll -= lean;
|
||||
state.setRotationInConstrainedFrame(glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
|
||||
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
|
||||
* glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
|
||||
* joint.rotation, DEFAULT_PRIORITY);
|
||||
_rig->setJointRotationInConstrainedFrame(index,
|
||||
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
|
||||
* glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
|
||||
* glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
|
||||
* joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index) {
|
||||
// likewise with the eye joints
|
||||
// NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual.
|
||||
glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() *
|
||||
glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
|
||||
glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() *
|
||||
glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
|
||||
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f));
|
||||
glm::vec3 lookAtDelta = _owningHead->getCorrectedLookAtPosition() - model->getTranslation();
|
||||
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * _owningHead->getSaccade(), 1.0f));
|
||||
glm::quat between = rotationBetween(front, lookAt);
|
||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||
state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
|
||||
joint.rotation, DEFAULT_PRIORITY);
|
||||
_rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between),
|
||||
-MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
|
||||
joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
void FaceModel::updateJointState(int index) {
|
||||
JointState& state = _jointStates[index];
|
||||
void FaceModel::maybeUpdateNeckAndEyeRotation(int index) {
|
||||
const JointState& state = _rig->getJointState(index);
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
||||
// guard against out-of-bounds access to _jointStates
|
||||
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
|
||||
const JointState& parentState = _jointStates.at(joint.parentIndex);
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _rig->getJointStateCount()) {
|
||||
const JointState& parentState = _rig->getJointState(joint.parentIndex);
|
||||
if (index == geometry.neckJointIndex) {
|
||||
maybeUpdateNeckRotation(parentState, joint, state);
|
||||
|
||||
maybeUpdateNeckRotation(parentState, joint, index);
|
||||
|
||||
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
|
||||
maybeUpdateEyeRotation(this, parentState, joint, state);
|
||||
maybeUpdateEyeRotation(this, parentState, joint, index);
|
||||
}
|
||||
}
|
||||
|
||||
Model::updateJointState(index);
|
||||
}
|
||||
|
||||
bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||
|
|
|
@ -19,23 +19,23 @@ class Head;
|
|||
/// A face formed from a linear mix of blendshapes according to a set of coefficients.
|
||||
class FaceModel : public Model {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
|
||||
FaceModel(Head* owningHead);
|
||||
FaceModel(Head* owningHead, RigPointer rig);
|
||||
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||
|
||||
virtual void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
virtual void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
virtual void updateJointState(int index);
|
||||
|
||||
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, int index);
|
||||
void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, int index);
|
||||
void maybeUpdateNeckAndEyeRotation(int index);
|
||||
|
||||
/// Retrieve the positions of up to two eye meshes.
|
||||
/// \return whether or not both eye meshes were found
|
||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
Head* _owningHead;
|
||||
};
|
||||
|
||||
|
|
|
@ -40,65 +40,6 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
}
|
||||
}
|
||||
|
||||
// We create a static CollisionList that is recycled for each collision test.
|
||||
const float MAX_COLLISIONS_PER_AVATAR = 32;
|
||||
static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR);
|
||||
|
||||
void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) {
|
||||
if (!avatar || avatar == _owningAvatar) {
|
||||
// don't collide hands against ourself (that is done elsewhere)
|
||||
return;
|
||||
}
|
||||
|
||||
const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel();
|
||||
int jointIndices[2];
|
||||
jointIndices[0] = skeletonModel.getLeftHandJointIndex();
|
||||
jointIndices[1] = skeletonModel.getRightHandJointIndex();
|
||||
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
int jointIndex = jointIndices[i];
|
||||
if (jointIndex < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handCollisions.clear();
|
||||
QVector<const Shape*> shapes;
|
||||
skeletonModel.getHandShapes(jointIndex, shapes);
|
||||
|
||||
if (avatar->findCollisions(shapes, handCollisions)) {
|
||||
glm::vec3 totalPenetration(0.0f);
|
||||
glm::vec3 averageContactPoint;
|
||||
for (int j = 0; j < handCollisions.size(); ++j) {
|
||||
CollisionInfo* collision = handCollisions.getCollision(j);
|
||||
totalPenetration += collision->_penetration;
|
||||
averageContactPoint += collision->_contactPoint;
|
||||
}
|
||||
if (isMyHand) {
|
||||
// our hand against other avatar
|
||||
// TODO: resolve this penetration when we don't think the other avatar will yield
|
||||
//palm.addToPenetration(averagePenetration);
|
||||
} else {
|
||||
// someone else's hand against MyAvatar
|
||||
// TODO: submit collision info to MyAvatar which should lean accordingly
|
||||
averageContactPoint /= (float)handCollisions.size();
|
||||
avatar->applyCollision(averageContactPoint, totalPenetration);
|
||||
|
||||
CollisionInfo collision;
|
||||
collision._penetration = totalPenetration;
|
||||
collision._contactPoint = averageContactPoint;
|
||||
emit avatar->collisionWithAvatar(avatar->getSessionUUID(), _owningAvatar->getSessionUUID(), collision);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hand::resolvePenetrations() {
|
||||
for (size_t i = 0; i < getNumPalms(); ++i) {
|
||||
PalmData& palm = getPalms()[i];
|
||||
palm.resolvePenetrations();
|
||||
}
|
||||
}
|
||||
|
||||
void Hand::render(RenderArgs* renderArgs, bool isMine) {
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE &&
|
||||
|
|
|
@ -29,10 +29,6 @@
|
|||
|
||||
class Avatar;
|
||||
|
||||
const float HAND_PADDLE_OFFSET = 0.1f;
|
||||
const float HAND_PADDLE_THICKNESS = 0.01f;
|
||||
const float HAND_PADDLE_RADIUS = 0.15f;
|
||||
|
||||
class Hand : public HandData {
|
||||
public:
|
||||
Hand(Avatar* owningAvatar);
|
||||
|
@ -40,10 +36,6 @@ public:
|
|||
void simulate(float deltaTime, bool isMine);
|
||||
void render(RenderArgs* renderArgs, bool isMine);
|
||||
|
||||
void collideAgainstAvatar(Avatar* avatar, bool isMyHand);
|
||||
|
||||
void resolvePenetrations();
|
||||
|
||||
private:
|
||||
// disallow copies of the Hand, copy of owning Avatar is disallowed too
|
||||
Hand(const Hand&);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "Util.h"
|
||||
#include "devices/DdeFaceTracker.h"
|
||||
#include "devices/Faceshift.h"
|
||||
#include "AvatarRig.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -57,11 +58,10 @@ Head::Head(Avatar* owningAvatar) :
|
|||
_isLookingAtMe(false),
|
||||
_lookingAtMeStarted(0),
|
||||
_wasLastLookingAtMe(0),
|
||||
_faceModel(this),
|
||||
_faceModel(this, std::make_shared<AvatarRig>()),
|
||||
_leftEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID()),
|
||||
_rightEyeLookAtID(DependencyManager::get<GeometryCache>()->allocateID())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Head::init() {
|
||||
|
|
|
@ -78,8 +78,8 @@ const float MyAvatar::ZOOM_MIN = 0.5f;
|
|||
const float MyAvatar::ZOOM_MAX = 25.0f;
|
||||
const float MyAvatar::ZOOM_DEFAULT = 1.5f;
|
||||
|
||||
MyAvatar::MyAvatar() :
|
||||
Avatar(),
|
||||
MyAvatar::MyAvatar(RigPointer rig) :
|
||||
Avatar(rig),
|
||||
_gravity(0.0f, 0.0f, 0.0f),
|
||||
_wasPushing(false),
|
||||
_isPushing(false),
|
||||
|
@ -101,18 +101,14 @@ MyAvatar::MyAvatar() :
|
|||
_eyeContactTarget(LEFT_EYE),
|
||||
_realWorldFieldOfView("realWorldFieldOfView",
|
||||
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
|
||||
_firstPersonSkeletonModel(this),
|
||||
_rig(rig),
|
||||
_prevShouldDrawHead(true)
|
||||
{
|
||||
_firstPersonSkeletonModel.setIsFirstPerson(true);
|
||||
|
||||
ShapeCollider::initDispatchTable();
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||
_driveKeys[i] = 0.0f;
|
||||
}
|
||||
|
||||
_skeletonModel.setEnableShapes(true);
|
||||
|
||||
// connect to AddressManager signal for location jumps
|
||||
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
|
||||
this, &MyAvatar::goToLocation);
|
||||
|
@ -139,7 +135,6 @@ QByteArray MyAvatar::toByteArray() {
|
|||
|
||||
void MyAvatar::reset() {
|
||||
_skeletonModel.reset();
|
||||
_firstPersonSkeletonModel.reset();
|
||||
getHead()->reset();
|
||||
|
||||
_targetVelocity = glm::vec3(0.0f);
|
||||
|
@ -195,7 +190,6 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
{
|
||||
PerformanceTimer perfTimer("skeleton");
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
_firstPersonSkeletonModel.simulate(deltaTime);
|
||||
}
|
||||
|
||||
if (!_skeletonModel.hasSkeleton()) {
|
||||
|
@ -211,10 +205,10 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
{
|
||||
PerformanceTimer perfTimer("joints");
|
||||
// copy out the skeleton joints from the model
|
||||
_jointData.resize(_skeletonModel.getJointStateCount());
|
||||
_jointData.resize(_rig->getJointStateCount());
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
JointData& data = _jointData[i];
|
||||
data.valid = _skeletonModel.getJointState(i, data.rotation);
|
||||
data.valid = _rig->getJointStateRotation(i, data.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,17 +485,6 @@ void MyAvatar::loadLastRecording() {
|
|||
_player->loadRecording(_recorder->getRecording());
|
||||
}
|
||||
|
||||
AnimationHandlePointer MyAvatar::addAnimationHandle() {
|
||||
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
|
||||
_animationHandles.append(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
void MyAvatar::removeAnimationHandle(const AnimationHandlePointer& handle) {
|
||||
handle->stop();
|
||||
_animationHandles.removeOne(handle);
|
||||
}
|
||||
|
||||
void MyAvatar::startAnimation(const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
@ -510,16 +493,7 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority,
|
|||
Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints));
|
||||
return;
|
||||
}
|
||||
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
|
||||
handle->setURL(url);
|
||||
handle->setFPS(fps);
|
||||
handle->setPriority(priority);
|
||||
handle->setLoop(loop);
|
||||
handle->setHold(hold);
|
||||
handle->setFirstFrame(firstFrame);
|
||||
handle->setLastFrame(lastFrame);
|
||||
handle->setMaskedJoints(maskedJoints);
|
||||
handle->start();
|
||||
_rig->startAnimation(url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints);
|
||||
}
|
||||
|
||||
void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
|
||||
|
@ -530,25 +504,7 @@ void MyAvatar::startAnimationByRole(const QString& role, const QString& url, flo
|
|||
Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints));
|
||||
return;
|
||||
}
|
||||
// check for a configured animation for the role
|
||||
foreach (const AnimationHandlePointer& handle, _animationHandles) {
|
||||
if (handle->getRole() == role) {
|
||||
handle->start();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// no joy; use the parameters provided
|
||||
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
|
||||
handle->setRole(role);
|
||||
handle->setURL(url);
|
||||
handle->setFPS(fps);
|
||||
handle->setPriority(priority);
|
||||
handle->setLoop(loop);
|
||||
handle->setHold(hold);
|
||||
handle->setFirstFrame(firstFrame);
|
||||
handle->setLastFrame(lastFrame);
|
||||
handle->setMaskedJoints(maskedJoints);
|
||||
handle->start();
|
||||
_rig->startAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints);
|
||||
}
|
||||
|
||||
void MyAvatar::stopAnimationByRole(const QString& role) {
|
||||
|
@ -556,11 +512,7 @@ void MyAvatar::stopAnimationByRole(const QString& role) {
|
|||
QMetaObject::invokeMethod(this, "stopAnimationByRole", Q_ARG(const QString&, role));
|
||||
return;
|
||||
}
|
||||
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
|
||||
if (handle->getRole() == role) {
|
||||
handle->stop();
|
||||
}
|
||||
}
|
||||
_rig->stopAnimationByRole(role);
|
||||
}
|
||||
|
||||
void MyAvatar::stopAnimation(const QString& url) {
|
||||
|
@ -568,11 +520,7 @@ void MyAvatar::stopAnimation(const QString& url) {
|
|||
QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url));
|
||||
return;
|
||||
}
|
||||
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
|
||||
if (handle->getURL() == url) {
|
||||
handle->stop();
|
||||
}
|
||||
}
|
||||
_rig->stopAnimation(url);
|
||||
}
|
||||
|
||||
AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) {
|
||||
|
@ -583,7 +531,7 @@ AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) {
|
|||
Q_ARG(const QString&, role));
|
||||
return result;
|
||||
}
|
||||
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
|
||||
foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) {
|
||||
if (handle->getRole() == role) {
|
||||
result = handle->getAnimationDetails();
|
||||
break;
|
||||
|
@ -600,7 +548,7 @@ AnimationDetails MyAvatar::getAnimationDetails(const QString& url) {
|
|||
Q_ARG(const QString&, url));
|
||||
return result;
|
||||
}
|
||||
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
|
||||
foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) {
|
||||
if (handle->getURL() == url) {
|
||||
result = handle->getAnimationDetails();
|
||||
break;
|
||||
|
@ -646,9 +594,10 @@ void MyAvatar::saveData() {
|
|||
settings.endArray();
|
||||
|
||||
settings.beginWriteArray("animationHandles");
|
||||
for (int i = 0; i < _animationHandles.size(); i++) {
|
||||
auto animationHandles = _rig->getAnimationHandles();
|
||||
for (int i = 0; i < animationHandles.size(); i++) {
|
||||
settings.setArrayIndex(i);
|
||||
const AnimationHandlePointer& pointer = _animationHandles.at(i);
|
||||
const AnimationHandlePointer& pointer = animationHandles.at(i);
|
||||
settings.setValue("role", pointer->getRole());
|
||||
settings.setValue("url", pointer->getURL());
|
||||
settings.setValue("fps", pointer->getFPS());
|
||||
|
@ -766,25 +715,19 @@ void MyAvatar::loadData() {
|
|||
setAttachmentData(attachmentData);
|
||||
|
||||
int animationCount = settings.beginReadArray("animationHandles");
|
||||
while (_animationHandles.size() > animationCount) {
|
||||
_animationHandles.takeLast()->stop();
|
||||
}
|
||||
while (_animationHandles.size() < animationCount) {
|
||||
addAnimationHandle();
|
||||
}
|
||||
_rig->deleteAnimations();
|
||||
for (int i = 0; i < animationCount; i++) {
|
||||
settings.setArrayIndex(i);
|
||||
const AnimationHandlePointer& handle = _animationHandles.at(i);
|
||||
handle->setRole(settings.value("role", "idle").toString());
|
||||
handle->setURL(settings.value("url").toUrl());
|
||||
handle->setFPS(loadSetting(settings, "fps", 30.0f));
|
||||
handle->setPriority(loadSetting(settings, "priority", 1.0f));
|
||||
handle->setLoop(settings.value("loop", true).toBool());
|
||||
handle->setHold(settings.value("hold", false).toBool());
|
||||
handle->setFirstFrame(settings.value("firstFrame", 0.0f).toFloat());
|
||||
handle->setLastFrame(settings.value("lastFrame", INT_MAX).toFloat());
|
||||
handle->setMaskedJoints(settings.value("maskedJoints").toStringList());
|
||||
handle->setStartAutomatically(settings.value("startAutomatically", true).toBool());
|
||||
_rig->addAnimationByRole(settings.value("role", "idle").toString(),
|
||||
settings.value("url").toString(),
|
||||
loadSetting(settings, "fps", 30.0f),
|
||||
loadSetting(settings, "priority", 1.0f),
|
||||
settings.value("loop", true).toBool(),
|
||||
settings.value("hold", false).toBool(),
|
||||
settings.value("firstFrame", 0.0f).toFloat(),
|
||||
settings.value("lastFrame", INT_MAX).toFloat(),
|
||||
settings.value("maskedJoints").toStringList(),
|
||||
settings.value("startAutomatically", true).toBool());
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
|
@ -792,6 +735,7 @@ void MyAvatar::loadData() {
|
|||
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
|
||||
|
||||
settings.endGroup();
|
||||
_rig->setEnableRig(settings.value("enableRig").toBool());
|
||||
}
|
||||
|
||||
void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
|
||||
|
@ -961,15 +905,15 @@ void MyAvatar::setJointRotations(QVector<glm::quat> jointRotations) {
|
|||
void MyAvatar::setJointData(int index, const glm::quat& rotation) {
|
||||
if (QThread::currentThread() == thread()) {
|
||||
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
|
||||
_skeletonModel.setJointState(index, true, rotation, SCRIPT_PRIORITY);
|
||||
_rig->setJointState(index, true, rotation, SCRIPT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointData(int index) {
|
||||
if (QThread::currentThread() == thread()) {
|
||||
// HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority
|
||||
_skeletonModel.setJointState(index, false, glm::quat(), 0.0f);
|
||||
_skeletonModel.clearJointAnimationPriority(index);
|
||||
_rig->setJointState(index, false, glm::quat(), 0.0f);
|
||||
_rig->clearJointAnimationPriority(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -980,7 +924,7 @@ void MyAvatar::clearJointsData() {
|
|||
void MyAvatar::clearJointAnimationPriorities() {
|
||||
int numStates = _skeletonModel.getJointStateCount();
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
_skeletonModel.clearJointAnimationPriority(i);
|
||||
_rig->clearJointAnimationPriority(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1020,19 +964,8 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
Avatar::setSkeletonModelURL(skeletonModelURL);
|
||||
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
||||
_billboardValid = false;
|
||||
|
||||
if (_useFullAvatar) {
|
||||
_skeletonModel.setVisibleInScene(_prevShouldDrawHead, scene);
|
||||
|
||||
const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst");
|
||||
_firstPersonSkeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
|
||||
_firstPersonSkeletonModel.setVisibleInScene(!_prevShouldDrawHead, scene);
|
||||
} else {
|
||||
_skeletonModel.setVisibleInScene(true, scene);
|
||||
|
||||
_firstPersonSkeletonModel.setVisibleInScene(false, scene);
|
||||
_firstPersonSkeletonModel.reset();
|
||||
}
|
||||
_skeletonModel.setVisibleInScene(true, scene);
|
||||
_headBoneSet.clear();
|
||||
}
|
||||
|
||||
void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) {
|
||||
|
@ -1143,11 +1076,10 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
|
|||
|
||||
void MyAvatar::rebuildSkeletonBody() {
|
||||
// compute localAABox
|
||||
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
|
||||
float radius = capsule.getRadius();
|
||||
float height = 2.0f * (capsule.getHalfHeight() + radius);
|
||||
float radius = _skeletonModel.getBoundingCapsuleRadius();
|
||||
float height = _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius;
|
||||
glm::vec3 corner(-radius, -0.5f * height, -radius);
|
||||
corner += _skeletonModel.getBoundingShapeOffset();
|
||||
corner += _skeletonModel.getBoundingCapsuleOffset();
|
||||
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
|
||||
_characterController.setLocalBoundingBox(corner, scale);
|
||||
}
|
||||
|
@ -1239,29 +1171,48 @@ void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::initHeadBones() {
|
||||
int neckJointIndex = -1;
|
||||
if (_skeletonModel.getGeometry()) {
|
||||
neckJointIndex = _skeletonModel.getGeometry()->getFBXGeometry().neckJointIndex;
|
||||
}
|
||||
if (neckJointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
_headBoneSet.clear();
|
||||
std::queue<int> q;
|
||||
q.push(neckJointIndex);
|
||||
_headBoneSet.insert(neckJointIndex);
|
||||
|
||||
// fbxJoints only hold links to parents not children, so we have to do a bit of extra work here.
|
||||
while (q.size() > 0) {
|
||||
int jointIndex = q.front();
|
||||
for (int i = 0; i < _skeletonModel.getJointStateCount(); i++) {
|
||||
if (jointIndex == _skeletonModel.getParentJointIndex(i)) {
|
||||
_headBoneSet.insert(i);
|
||||
q.push(i);
|
||||
}
|
||||
}
|
||||
q.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::preRender(RenderArgs* renderArgs) {
|
||||
|
||||
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
||||
const bool shouldDrawHead = shouldRenderHead(renderArgs);
|
||||
|
||||
_skeletonModel.initWhenReady(scene);
|
||||
if (_useFullAvatar) {
|
||||
_firstPersonSkeletonModel.initWhenReady(scene);
|
||||
if (_skeletonModel.initWhenReady(scene)) {
|
||||
initHeadBones();
|
||||
_skeletonModel.setCauterizeBoneSet(_headBoneSet);
|
||||
}
|
||||
|
||||
if (shouldDrawHead != _prevShouldDrawHead) {
|
||||
if (_useFullAvatar) {
|
||||
if (shouldDrawHead) {
|
||||
_skeletonModel.setVisibleInScene(true, scene);
|
||||
_firstPersonSkeletonModel.setVisibleInScene(false, scene);
|
||||
} else {
|
||||
_skeletonModel.setVisibleInScene(false, scene);
|
||||
_firstPersonSkeletonModel.setVisibleInScene(true, scene);
|
||||
}
|
||||
_skeletonModel.setCauterizeBones(!shouldDrawHead);
|
||||
} else {
|
||||
getHead()->getFaceModel().setVisibleInScene(shouldDrawHead, scene);
|
||||
}
|
||||
|
||||
}
|
||||
_prevShouldDrawHead = shouldDrawHead;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <SettingHandle.h>
|
||||
#include <DynamicCharacterController.h>
|
||||
#include <Rig.h>
|
||||
|
||||
#include "Avatar.h"
|
||||
|
||||
|
@ -35,54 +36,43 @@ class MyAvatar : public Avatar {
|
|||
//TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
|
||||
|
||||
public:
|
||||
MyAvatar();
|
||||
MyAvatar(RigPointer rig);
|
||||
~MyAvatar();
|
||||
|
||||
QByteArray toByteArray();
|
||||
|
||||
void reset();
|
||||
void update(float deltaTime);
|
||||
void simulate(float deltaTime);
|
||||
void preRender(RenderArgs* renderArgs);
|
||||
void updateFromTrackers(float deltaTime);
|
||||
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
|
||||
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override;
|
||||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
|
||||
|
||||
// setters
|
||||
void setLeanScale(float scale) { _leanScale = scale; }
|
||||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
|
||||
void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); }
|
||||
|
||||
// getters
|
||||
float getLeanScale() const { return _leanScale; }
|
||||
Q_INVOKABLE glm::vec3 getDefaultEyePosition() const;
|
||||
bool getShouldRenderLocally() const { return _shouldRender; }
|
||||
|
||||
float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); }
|
||||
|
||||
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
|
||||
AnimationHandlePointer addAnimationHandle();
|
||||
void removeAnimationHandle(const AnimationHandlePointer& handle);
|
||||
|
||||
|
||||
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _rig->getAnimationHandles(); }
|
||||
AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); }
|
||||
void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); }
|
||||
/// Allows scripts to run animations.
|
||||
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
|
||||
bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
|
||||
bool hold = false, float firstFrame = 0.0f,
|
||||
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
/// Stops an animation as identified by a URL.
|
||||
Q_INVOKABLE void stopAnimation(const QString& url);
|
||||
|
||||
|
||||
/// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom
|
||||
/// animation for the role.
|
||||
Q_INVOKABLE void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
|
||||
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
|
||||
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
|
||||
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
|
||||
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
/// Stops an animation identified by its role.
|
||||
Q_INVOKABLE void stopAnimationByRole(const QString& role);
|
||||
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
|
||||
|
||||
void clearJointAnimationPriorities();
|
||||
|
||||
// get/set avatar data
|
||||
void saveData();
|
||||
void loadData();
|
||||
|
@ -93,32 +83,27 @@ public:
|
|||
// Set what driving keys are being pressed to control thrust levels
|
||||
void clearDriveKeys();
|
||||
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
|
||||
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
|
||||
|
||||
void relayDriveKeysToCharacterController();
|
||||
|
||||
bool isMyAvatar() const { return true; }
|
||||
|
||||
eyeContactTarget getEyeContactTarget();
|
||||
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer);
|
||||
|
||||
static void sendKillAvatar();
|
||||
|
||||
|
||||
Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; }
|
||||
Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); }
|
||||
Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); }
|
||||
Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); }
|
||||
Q_INVOKABLE float getHeadFinalPitch() const { return getHead()->getFinalPitch(); }
|
||||
Q_INVOKABLE float getHeadDeltaPitch() const { return getHead()->getDeltaPitch(); }
|
||||
|
||||
|
||||
Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); }
|
||||
|
||||
|
||||
Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
|
||||
|
||||
AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
|
||||
void updateLookAtTargetAvatar();
|
||||
void clearLookAtTargetAvatar();
|
||||
|
||||
|
||||
virtual void setJointRotations(QVector<glm::quat> jointRotations);
|
||||
virtual void setJointData(int index, const glm::quat& rotation);
|
||||
virtual void clearJointData(int index);
|
||||
|
@ -127,7 +112,8 @@ public:
|
|||
Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString());
|
||||
Q_INVOKABLE void useHeadURL(const QUrl& headURL, const QString& modelName = QString());
|
||||
Q_INVOKABLE void useBodyURL(const QUrl& bodyURL, const QString& modelName = QString());
|
||||
Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName = QString(), const QString& bodyName = QString());
|
||||
Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL,
|
||||
const QString& headName = QString(), const QString& bodyName = QString());
|
||||
|
||||
Q_INVOKABLE bool getUseFullAvatar() const { return _useFullAvatar; }
|
||||
Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
|
||||
|
@ -142,48 +128,30 @@ public:
|
|||
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
|
||||
virtual glm::vec3 getSkeletonPosition() const;
|
||||
void updateLocalAABox();
|
||||
DynamicCharacterController* getCharacterController() { return &_characterController; }
|
||||
|
||||
void clearJointAnimationPriorities();
|
||||
|
||||
glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; }
|
||||
float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; }
|
||||
QString getScriptedMotorFrame() const;
|
||||
|
||||
void setScriptedMotorVelocity(const glm::vec3& velocity);
|
||||
void setScriptedMotorTimescale(float timescale);
|
||||
void setScriptedMotorFrame(QString frame);
|
||||
|
||||
const QString& getCollisionSoundURL() {return _collisionSoundURL; }
|
||||
void setCollisionSoundURL(const QString& url);
|
||||
|
||||
void clearScriptableSettings();
|
||||
|
||||
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
|
||||
bool allowDuplicates = false, bool useSaved = true);
|
||||
|
||||
/// Renders a laser pointer for UI picking
|
||||
void renderLaserPointers(gpu::Batch& batch);
|
||||
|
||||
glm::vec3 getLaserPointerTipPosition(const PalmData* palm);
|
||||
|
||||
const RecorderPointer getRecorder() const { return _recorder; }
|
||||
const PlayerPointer getPlayer() const { return _player; }
|
||||
|
||||
|
||||
float getBoomLength() const { return _boomLength; }
|
||||
void setBoomLength(float boomLength) { _boomLength = boomLength; }
|
||||
|
||||
|
||||
static const float ZOOM_MIN;
|
||||
static const float ZOOM_MAX;
|
||||
static const float ZOOM_DEFAULT;
|
||||
|
||||
|
||||
public slots:
|
||||
void increaseSize();
|
||||
void decreaseSize();
|
||||
void resetSize();
|
||||
|
||||
|
||||
void goToLocation(const glm::vec3& newPosition,
|
||||
bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(),
|
||||
bool shouldFaceLocation = false);
|
||||
|
@ -203,7 +171,7 @@ public slots:
|
|||
void clearReferential();
|
||||
bool setModelReferential(const QUuid& id);
|
||||
bool setJointReferential(const QUuid& id, int jointIndex);
|
||||
|
||||
|
||||
bool isRecording();
|
||||
qint64 recorderElapsed();
|
||||
void startRecording();
|
||||
|
@ -212,7 +180,7 @@ public slots:
|
|||
void loadLastRecording();
|
||||
|
||||
virtual void rebuildSkeletonBody();
|
||||
|
||||
|
||||
signals:
|
||||
void transformChanged();
|
||||
void newCollisionSoundURL(const QUrl& url);
|
||||
|
@ -220,6 +188,33 @@ signals:
|
|||
|
||||
private:
|
||||
|
||||
QByteArray toByteArray();
|
||||
void simulate(float deltaTime);
|
||||
void updateFromTrackers(float deltaTime);
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
|
||||
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override;
|
||||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
|
||||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
|
||||
bool getShouldRenderLocally() const { return _shouldRender; }
|
||||
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
|
||||
bool isMyAvatar() const { return true; }
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer);
|
||||
virtual glm::vec3 getSkeletonPosition() const;
|
||||
|
||||
glm::vec3 getScriptedMotorVelocity() const { return _scriptedMotorVelocity; }
|
||||
float getScriptedMotorTimescale() const { return _scriptedMotorTimescale; }
|
||||
QString getScriptedMotorFrame() const;
|
||||
void setScriptedMotorVelocity(const glm::vec3& velocity);
|
||||
void setScriptedMotorTimescale(float timescale);
|
||||
void setScriptedMotorFrame(QString frame);
|
||||
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
|
||||
bool allowDuplicates = false, bool useSaved = true);
|
||||
|
||||
void renderLaserPointers(gpu::Batch& batch);
|
||||
const RecorderPointer getRecorder() const { return _recorder; }
|
||||
const PlayerPointer getPlayer() const { return _player; }
|
||||
|
||||
bool cameraInsideHead() const;
|
||||
|
||||
// These are made private for MyAvatar so that you will use the "use" methods instead
|
||||
|
@ -234,7 +229,7 @@ private:
|
|||
bool _wasPushing;
|
||||
bool _isPushing;
|
||||
bool _isBraking;
|
||||
|
||||
|
||||
float _boomLength;
|
||||
|
||||
float _trapDuration; // seconds that avatar has been trapped by collisions
|
||||
|
@ -256,37 +251,36 @@ private:
|
|||
bool _billboardValid;
|
||||
float _oculusYawOffset;
|
||||
|
||||
QList<AnimationHandlePointer> _animationHandles;
|
||||
|
||||
eyeContactTarget _eyeContactTarget;
|
||||
|
||||
RecorderPointer _recorder;
|
||||
|
||||
|
||||
glm::vec3 _trackedHeadPosition;
|
||||
|
||||
|
||||
Setting::Handle<float> _realWorldFieldOfView;
|
||||
|
||||
// private methods
|
||||
|
||||
// private methods
|
||||
void updateOrientation(float deltaTime);
|
||||
glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool isHovering);
|
||||
glm::vec3 applyScriptedMotor(float deltaTime, const glm::vec3& velocity);
|
||||
void updatePosition(float deltaTime);
|
||||
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||
void maybeUpdateBillboard();
|
||||
|
||||
void initHeadBones();
|
||||
|
||||
// Avatar Preferences
|
||||
bool _useFullAvatar = false;
|
||||
QUrl _fullAvatarURLFromPreferences;
|
||||
QUrl _headURLFromPreferences;
|
||||
QUrl _skeletonURLFromPreferences;
|
||||
|
||||
|
||||
QString _headModelName;
|
||||
QString _bodyModelName;
|
||||
QString _fullAvatarModelName;
|
||||
|
||||
// used for rendering when in first person view or when in an HMD.
|
||||
SkeletonModel _firstPersonSkeletonModel;
|
||||
RigPointer _rig;
|
||||
bool _prevShouldDrawHead;
|
||||
std::unordered_set<int> _headBoneSet;
|
||||
};
|
||||
|
||||
#endif // hifi_MyAvatar_h
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
#include <glm/gtx/transform.hpp>
|
||||
#include <QMultiMap>
|
||||
|
||||
#include <CapsuleShape.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <SphereShape.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
|
@ -24,29 +22,31 @@
|
|||
#include "Util.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
||||
Model(parent),
|
||||
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) :
|
||||
Model(rig, parent),
|
||||
_triangleFanID(DependencyManager::get<GeometryCache>()->allocateID()),
|
||||
_owningAvatar(owningAvatar),
|
||||
_boundingShape(),
|
||||
_boundingShapeLocalOffset(0.0f),
|
||||
_boundingCapsuleLocalOffset(0.0f),
|
||||
_boundingCapsuleRadius(0.0f),
|
||||
_boundingCapsuleHeight(0.0f),
|
||||
_defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
_headClipDistance(DEFAULT_NEAR_CLIP),
|
||||
_isFirstPerson(false)
|
||||
_headClipDistance(DEFAULT_NEAR_CLIP)
|
||||
{
|
||||
assert(_rig);
|
||||
assert(_owningAvatar);
|
||||
_enableShapes = true;
|
||||
}
|
||||
|
||||
SkeletonModel::~SkeletonModel() {
|
||||
}
|
||||
|
||||
void SkeletonModel::initJointStates(QVector<JointState> states) {
|
||||
Model::initJointStates(states);
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
_boundingRadius = _rig->initJointStates(states, parentTransform);
|
||||
|
||||
// Determine the default eye position for avatar scale = 1.0
|
||||
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
|
||||
if (0 <= headJointIndex && headJointIndex < _jointStates.size()) {
|
||||
if (0 <= headJointIndex && headJointIndex < _rig->getJointStateCount()) {
|
||||
|
||||
glm::vec3 leftEyePosition, rightEyePosition;
|
||||
getEyeModelPositions(leftEyePosition, rightEyePosition);
|
||||
|
@ -66,14 +66,11 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
|
|||
// the SkeletonModel override of updateJointState() will clear the translation part
|
||||
// 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 < _jointStates.size(); i++) {
|
||||
updateJointState(i);
|
||||
for (int i = 0; i < _rig->getJointStateCount(); i++) {
|
||||
_rig->updateJointState(i, parentTransform);
|
||||
}
|
||||
|
||||
clearShapes();
|
||||
if (_enableShapes) {
|
||||
buildShapes();
|
||||
}
|
||||
buildShapes();
|
||||
|
||||
Extents meshExtents = getMeshExtents();
|
||||
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
|
||||
|
@ -86,6 +83,29 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
|
|||
const float PALM_PRIORITY = DEFAULT_PRIORITY;
|
||||
const float LEAN_PRIORITY = DEFAULT_PRIORITY;
|
||||
|
||||
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||
_rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
|
||||
Model::updateRig(deltaTime, parentTransform);
|
||||
if (_owningAvatar->isMyAvatar()) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
||||
Rig::HeadParameters params;
|
||||
params.leanSideways = _owningAvatar->getHead()->getFinalLeanSideways();
|
||||
params.leanForward = _owningAvatar->getHead()->getFinalLeanSideways();
|
||||
params.torsoTwist = _owningAvatar->getHead()->getTorsoTwist();
|
||||
params.localHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInLocalFrame();
|
||||
params.worldHeadOrientation = _owningAvatar->getHead()->getFinalOrientationInWorldFrame();
|
||||
params.eyeLookAt = _owningAvatar->getHead()->getCorrectedLookAtPosition();
|
||||
params.eyeSaccade = _owningAvatar->getHead()->getSaccade();
|
||||
params.leanJointIndex = geometry.leanJointIndex;
|
||||
params.neckJointIndex = geometry.neckJointIndex;
|
||||
params.leftEyeJointIndex = geometry.leftEyeJointIndex;
|
||||
params.rightEyeJointIndex = geometry.rightEyeJointIndex;
|
||||
|
||||
_rig->updateFromHeadParameters(params);
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
setTranslation(_owningAvatar->getSkeletonPosition());
|
||||
static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
@ -141,49 +161,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
if (_isFirstPerson) {
|
||||
cauterizeHead();
|
||||
updateClusterMatrices();
|
||||
}
|
||||
|
||||
_boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset);
|
||||
_boundingShape.setRotation(_rotation);
|
||||
}
|
||||
|
||||
void SkeletonModel::getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const {
|
||||
if (jointIndex < 0 || jointIndex >= int(_shapes.size())) {
|
||||
return;
|
||||
}
|
||||
if (jointIndex == getLeftHandJointIndex()
|
||||
|| jointIndex == getRightHandJointIndex()) {
|
||||
// get all shapes that have this hand as an ancestor in the skeleton heirarchy
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
const FBXJoint& joint = geometry.joints[i];
|
||||
int parentIndex = joint.parentIndex;
|
||||
Shape* shape = _shapes[i];
|
||||
if (i == jointIndex) {
|
||||
// this shape is the hand
|
||||
if (shape) {
|
||||
shapes.push_back(shape);
|
||||
}
|
||||
if (parentIndex != -1 && _shapes[parentIndex]) {
|
||||
// also add the forearm
|
||||
shapes.push_back(_shapes[parentIndex]);
|
||||
}
|
||||
} else if (shape) {
|
||||
while (parentIndex != -1) {
|
||||
if (parentIndex == jointIndex) {
|
||||
// this shape is a child of the hand
|
||||
shapes.push_back(shape);
|
||||
break;
|
||||
}
|
||||
parentIndex = geometry.joints[parentIndex].parentIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::renderIKConstraints(gpu::Batch& batch) {
|
||||
|
@ -202,7 +179,7 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) {
|
|||
}
|
||||
|
||||
void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||
return;
|
||||
}
|
||||
// NOTE: 'position' is in model-frame
|
||||
|
@ -217,16 +194,20 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
|||
if (forearmLength < EPSILON) {
|
||||
return;
|
||||
}
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
glm::quat handRotation = state.getRotation();
|
||||
glm::quat handRotation;
|
||||
if (!_rig->getJointStateRotation(jointIndex, handRotation)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// align hand with forearm
|
||||
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
|
||||
state.applyRotationDelta(rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), true, PALM_PRIORITY);
|
||||
_rig->applyJointRotationDelta(jointIndex,
|
||||
rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector),
|
||||
true, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||
return;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
@ -252,68 +233,16 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
|||
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);
|
||||
JointState& parentState = _jointStates[parentJointIndex];
|
||||
parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY);
|
||||
_rig->setJointRotationInBindFrame(parentJointIndex, palmRotation, PALM_PRIORITY);
|
||||
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
||||
_jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat(), PALM_PRIORITY);
|
||||
_rig->setJointRotationInConstrainedFrame(jointIndex, glm::quat(), PALM_PRIORITY);
|
||||
} else {
|
||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::updateJointState(int index) {
|
||||
if (index < 0 && index >= _jointStates.size()) {
|
||||
return; // bail
|
||||
}
|
||||
JointState& state = _jointStates[index];
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
|
||||
const JointState& parentState = _jointStates.at(joint.parentIndex);
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (index == geometry.leanJointIndex) {
|
||||
maybeUpdateLeanRotation(parentState, state);
|
||||
|
||||
} else if (index == geometry.neckJointIndex) {
|
||||
maybeUpdateNeckRotation(parentState, joint, state);
|
||||
|
||||
} else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
|
||||
maybeUpdateEyeRotation(parentState, joint, state);
|
||||
}
|
||||
}
|
||||
|
||||
Model::updateJointState(index);
|
||||
|
||||
if (index == _geometry->getFBXGeometry().rootJointIndex) {
|
||||
state.clearTransformTranslation();
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) {
|
||||
if (!_owningAvatar->isMyAvatar()) {
|
||||
return;
|
||||
}
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
||||
glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame());
|
||||
state.setRotationInConstrainedFrame(
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis)
|
||||
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis)
|
||||
* glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis)
|
||||
* state.getFBXJoint().rotation, LEAN_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
_owningAvatar->getHead()->getFaceModel().maybeUpdateNeckRotation(parentState, joint, state);
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
_owningAvatar->getHead()->getFaceModel().maybeUpdateEyeRotation(this, parentState, joint, state);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
|
||||
return;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
@ -322,9 +251,11 @@ void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {
|
|||
batch._glLineWidth(3.0f);
|
||||
do {
|
||||
const FBXJoint& joint = geometry.joints.at(jointIndex);
|
||||
const JointState& jointState = _jointStates.at(jointIndex);
|
||||
const JointState& jointState = _rig->getJointState(jointIndex);
|
||||
glm::vec3 position = _rotation * jointState.getPosition() + _translation;
|
||||
glm::quat parentRotation = (joint.parentIndex == -1) ? _rotation : _rotation * _jointStates.at(joint.parentIndex).getRotation();
|
||||
glm::quat parentRotation = (joint.parentIndex == -1) ?
|
||||
_rotation :
|
||||
_rotation * _rig->getJointState(joint.parentIndex).getRotation();
|
||||
float fanScale = directionSize * 0.75f;
|
||||
|
||||
Transform transform = Transform();
|
||||
|
@ -457,14 +388,11 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c
|
|||
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
|
||||
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
|
||||
|
||||
JointState& shoulderState = _jointStates[shoulderJointIndex];
|
||||
shoulderState.setRotationInBindFrame(shoulderRotation, PALM_PRIORITY);
|
||||
|
||||
JointState& elbowState = _jointStates[elbowJointIndex];
|
||||
elbowState.setRotationInBindFrame(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY);
|
||||
|
||||
JointState& handState = _jointStates[jointIndex];
|
||||
handState.setRotationInBindFrame(rotation, PALM_PRIORITY);
|
||||
_rig->setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, PALM_PRIORITY);
|
||||
_rig->setJointRotationInBindFrame(elbowJointIndex,
|
||||
rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) *
|
||||
shoulderRotation, PALM_PRIORITY);
|
||||
_rig->setJointRotationInBindFrame(jointIndex, rotation, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {
|
||||
|
@ -519,7 +447,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP
|
|||
glm::quat worldFrameRotation;
|
||||
bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation);
|
||||
if (success) {
|
||||
neckParentRotation = worldFrameRotation * _jointStates[parentIndex].getFBXJoint().inverseDefaultRotation;
|
||||
neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getFBXJoint().inverseDefaultRotation;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
@ -570,7 +498,7 @@ float VERY_BIG_MASS = 1.0e6f;
|
|||
|
||||
// virtual
|
||||
void SkeletonModel::buildShapes() {
|
||||
if (_geometry == NULL || _jointStates.isEmpty()) {
|
||||
if (_geometry == NULL || _rig->jointStatesEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -579,46 +507,12 @@ void SkeletonModel::buildShapes() {
|
|||
// rootJointIndex == -1 if the avatar model has no skeleton
|
||||
return;
|
||||
}
|
||||
|
||||
float uniformScale = extractUniformScale(_scale);
|
||||
const int numStates = _jointStates.size();
|
||||
for (int i = 0; i < numStates; i++) {
|
||||
JointState& state = _jointStates[i];
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
float radius = uniformScale * joint.boneRadius;
|
||||
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
|
||||
Shape::Type type = joint.shapeType;
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1 || radius < EPSILON) {
|
||||
type = INVALID_SHAPE;
|
||||
} else if (type == CAPSULE_SHAPE && halfHeight < EPSILON) {
|
||||
// this shape is forced to be a sphere
|
||||
type = SPHERE_SHAPE;
|
||||
}
|
||||
Shape* shape = NULL;
|
||||
if (type == SPHERE_SHAPE) {
|
||||
shape = new SphereShape(radius);
|
||||
shape->setEntity(this);
|
||||
} else if (type == CAPSULE_SHAPE) {
|
||||
assert(parentIndex != -1);
|
||||
shape = new CapsuleShape(radius, halfHeight);
|
||||
shape->setEntity(this);
|
||||
}
|
||||
if (shape && parentIndex != -1) {
|
||||
// always disable collisions between joint and its parent
|
||||
disableCollisions(i, parentIndex);
|
||||
}
|
||||
_shapes.push_back(shape);
|
||||
}
|
||||
|
||||
// This method moves the shapes to their default positions in Model frame.
|
||||
computeBoundingShape(geometry);
|
||||
}
|
||||
|
||||
void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
||||
// compute default joint transforms
|
||||
int numStates = _jointStates.size();
|
||||
assert(numStates == _shapes.size());
|
||||
int numStates = _rig->getJointStateCount();
|
||||
QVector<glm::mat4> transforms;
|
||||
transforms.fill(glm::mat4(), numStates);
|
||||
|
||||
|
@ -628,131 +522,63 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
|||
totalExtents.addPoint(glm::vec3(0.0f));
|
||||
for (int i = 0; i < numStates; i++) {
|
||||
// compute the default transform of this joint
|
||||
JointState& state = _jointStates[i];
|
||||
const JointState& state = _rig->getJointState(i);
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
transforms[i] = _jointStates[i].getTransform();
|
||||
transforms[i] = _rig->getJointTransform(i);
|
||||
} else {
|
||||
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
||||
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
|
||||
* joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform;
|
||||
}
|
||||
|
||||
// Each joint contributes its point to the bounding box
|
||||
// Each joint contributes a sphere at its position
|
||||
glm::vec3 axis(joint.boneRadius);
|
||||
glm::vec3 jointPosition = extractTranslation(transforms[i]);
|
||||
totalExtents.addPoint(jointPosition);
|
||||
|
||||
Shape* shape = _shapes[i];
|
||||
if (!shape) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Each joint with a shape contributes to the totalExtents: a box
|
||||
// that contains the sphere centered at the end of the joint with radius of the bone.
|
||||
|
||||
// TODO: skip hand and arm shapes for bounding box calculation
|
||||
int type = shape->getType();
|
||||
if (type == CAPSULE_SHAPE) {
|
||||
// add the two furthest surface points of the capsule
|
||||
CapsuleShape* capsule = static_cast<CapsuleShape*>(shape);
|
||||
float radius = capsule->getRadius();
|
||||
glm::vec3 axis(radius);
|
||||
Extents shapeExtents;
|
||||
shapeExtents.reset();
|
||||
shapeExtents.addPoint(jointPosition + axis);
|
||||
shapeExtents.addPoint(jointPosition - axis);
|
||||
totalExtents.addExtents(shapeExtents);
|
||||
} else if (type == SPHERE_SHAPE) {
|
||||
float radius = shape->getBoundingRadius();
|
||||
glm::vec3 axis(radius);
|
||||
Extents shapeExtents;
|
||||
shapeExtents.reset();
|
||||
shapeExtents.addPoint(jointPosition + axis);
|
||||
shapeExtents.addPoint(jointPosition - axis);
|
||||
totalExtents.addExtents(shapeExtents);
|
||||
}
|
||||
totalExtents.addPoint(jointPosition + axis);
|
||||
totalExtents.addPoint(jointPosition - axis);
|
||||
}
|
||||
|
||||
// compute bounding shape parameters
|
||||
// NOTE: we assume that the longest side of totalExtents is the yAxis...
|
||||
glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum;
|
||||
// ... and assume the radius is half the RMS of the X and Z sides:
|
||||
float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
|
||||
_boundingShape.setRadius(capsuleRadius);
|
||||
_boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius);
|
||||
_boundingCapsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
|
||||
_boundingCapsuleHeight = diagonal.y - 2.0f * _boundingCapsuleRadius;
|
||||
|
||||
glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition();
|
||||
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
|
||||
glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition();
|
||||
_boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
|
||||
_boundingRadius = 0.5f * glm::length(diagonal);
|
||||
}
|
||||
|
||||
void SkeletonModel::resetShapePositionsToDefaultPose() {
|
||||
// DEBUG method.
|
||||
// Moves shapes to the joint default locations for debug visibility into
|
||||
// how the bounding shape is computed.
|
||||
|
||||
if (!_geometry || _shapes.isEmpty()) {
|
||||
// geometry or joints have not yet been created
|
||||
return;
|
||||
}
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (geometry.joints.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The shapes are moved to their default positions in computeBoundingShape().
|
||||
computeBoundingShape(geometry);
|
||||
|
||||
// Then we move them into world frame for rendering at the Model's location.
|
||||
for (int i = 0; i < _shapes.size(); i++) {
|
||||
Shape* shape = _shapes[i];
|
||||
if (shape) {
|
||||
shape->setTranslation(_translation + _rotation * shape->getTranslation());
|
||||
shape->setRotation(_rotation * shape->getRotation());
|
||||
}
|
||||
}
|
||||
_boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset);
|
||||
_boundingShape.setRotation(_rotation);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) {
|
||||
const int BALL_SUBDIVISIONS = 10;
|
||||
if (_shapes.isEmpty()) {
|
||||
// the bounding shape has not been properly computed
|
||||
// so no need to render it
|
||||
return;
|
||||
}
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
|
||||
Transform transform; // = Transform();
|
||||
|
||||
// draw a blue sphere at the capsule end point
|
||||
glm::vec3 endPoint;
|
||||
_boundingShape.getEndPoint(endPoint);
|
||||
endPoint = endPoint + _translation;
|
||||
transform.setTranslation(endPoint);
|
||||
// draw a blue sphere at the capsule top point
|
||||
glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
transform.setTranslation(topPoint);
|
||||
batch.setModelTransform(transform);
|
||||
deferredLighting->bindSimpleProgram(batch);
|
||||
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||
|
||||
// draw a yellow sphere at the capsule start point
|
||||
glm::vec3 startPoint;
|
||||
_boundingShape.getStartPoint(startPoint);
|
||||
startPoint = startPoint + _translation;
|
||||
glm::vec3 axis = endPoint - startPoint;
|
||||
transform.setTranslation(startPoint);
|
||||
// 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);
|
||||
batch.setModelTransform(transform);
|
||||
deferredLighting->bindSimpleProgram(batch);
|
||||
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
glm::vec4(0.8f, 0.8f, 0.6f, alpha));
|
||||
|
||||
// draw a green cylinder between the two points
|
||||
glm::vec3 origin(0.0f);
|
||||
Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(),
|
||||
Avatar::renderJointConnectingCone(batch, origin, axis, _boundingCapsuleRadius, _boundingCapsuleRadius,
|
||||
glm::vec4(0.6f, 0.8f, 0.6f, alpha));
|
||||
}
|
||||
|
||||
|
@ -760,56 +586,5 @@ bool SkeletonModel::hasSkeleton() {
|
|||
return isActive() ? _geometry->getFBXGeometry().rootJointIndex != -1 : false;
|
||||
}
|
||||
|
||||
void SkeletonModel::initHeadBones() {
|
||||
_headBones.clear();
|
||||
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
|
||||
const int neckJointIndex = fbxGeometry.neckJointIndex;
|
||||
std::queue<int> q;
|
||||
q.push(neckJointIndex);
|
||||
_headBones.push_back(neckJointIndex);
|
||||
|
||||
// fbxJoints only hold links to parents not children, so we have to do a bit of extra work here.
|
||||
while (q.size() > 0) {
|
||||
int jointIndex = q.front();
|
||||
for (int i = 0; i < fbxGeometry.joints.size(); i++) {
|
||||
const FBXJoint& fbxJoint = fbxGeometry.joints[i];
|
||||
if (jointIndex == fbxJoint.parentIndex) {
|
||||
_headBones.push_back(i);
|
||||
q.push(i);
|
||||
}
|
||||
}
|
||||
q.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::invalidateHeadBones() {
|
||||
_headBones.clear();
|
||||
}
|
||||
|
||||
void SkeletonModel::cauterizeHead() {
|
||||
if (isActive()) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const int neckJointIndex = geometry.neckJointIndex;
|
||||
if (neckJointIndex > 0 && neckJointIndex < _jointStates.size()) {
|
||||
|
||||
// lazy init of headBones
|
||||
if (_headBones.size() == 0) {
|
||||
initHeadBones();
|
||||
}
|
||||
|
||||
// preserve the translation for the neck
|
||||
glm::vec4 trans = _jointStates[neckJointIndex].getTransform()[3];
|
||||
glm::vec4 zero(0, 0, 0, 0);
|
||||
for (const int &i : _headBones) {
|
||||
JointState& joint = _jointStates[i];
|
||||
glm::mat4 newXform(zero, zero, zero, trans);
|
||||
joint.setTransform(newXform);
|
||||
joint.setVisibleTransform(newXform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::onInvalidate() {
|
||||
invalidateHeadBones();
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_SkeletonModel_h
|
||||
|
||||
|
||||
#include <CapsuleShape.h>
|
||||
#include <Model.h>
|
||||
|
||||
class Avatar;
|
||||
|
@ -22,72 +21,69 @@ class MuscleConstraint;
|
|||
/// A skeleton loaded from a model.
|
||||
class SkeletonModel : public Model {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
|
||||
SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL);
|
||||
SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr);
|
||||
~SkeletonModel();
|
||||
|
||||
|
||||
virtual void initJointStates(QVector<JointState> states);
|
||||
|
||||
void simulate(float deltaTime, bool fullUpdate = true);
|
||||
|
||||
/// \param jointIndex index of hand joint
|
||||
/// \param shapes[out] list in which is stored pointers to hand shapes
|
||||
void getHandShapes(int jointIndex, QVector<const Shape*>& shapes) const;
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
||||
|
||||
void renderIKConstraints(gpu::Batch& batch);
|
||||
|
||||
|
||||
/// Returns the index of the left hand joint, or -1 if not found.
|
||||
int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; }
|
||||
|
||||
|
||||
/// Returns the index of the right hand joint, or -1 if not found.
|
||||
int getRightHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().rightHandJointIndex : -1; }
|
||||
|
||||
/// Retrieve the position of the left hand
|
||||
/// \return true whether or not the position was found
|
||||
bool getLeftHandPosition(glm::vec3& position) const;
|
||||
|
||||
|
||||
/// Retrieve the position of the right hand
|
||||
/// \return true whether or not the position was found
|
||||
bool getRightHandPosition(glm::vec3& position) const;
|
||||
|
||||
|
||||
/// Restores some fraction of the default position of the left hand.
|
||||
/// \param fraction the fraction of the default position to restore
|
||||
/// \return whether or not the left hand joint was found
|
||||
bool restoreLeftHandPosition(float fraction = 1.0f, float priority = 1.0f);
|
||||
|
||||
|
||||
/// Gets the position of the left shoulder.
|
||||
/// \return whether or not the left shoulder joint was found
|
||||
bool getLeftShoulderPosition(glm::vec3& position) const;
|
||||
|
||||
|
||||
/// Returns the extended length from the left hand to its last free ancestor.
|
||||
float getLeftArmLength() const;
|
||||
|
||||
|
||||
/// Restores some fraction of the default position of the right hand.
|
||||
/// \param fraction the fraction of the default position to restore
|
||||
/// \return whether or not the right hand joint was found
|
||||
bool restoreRightHandPosition(float fraction = 1.0f, float priority = 1.0f);
|
||||
|
||||
|
||||
/// Gets the position of the right shoulder.
|
||||
/// \return whether or not the right shoulder joint was found
|
||||
bool getRightShoulderPosition(glm::vec3& position) const;
|
||||
|
||||
|
||||
/// Returns the extended length from the right hand to its first free ancestor.
|
||||
float getRightArmLength() const;
|
||||
|
||||
/// Returns the position of the head joint.
|
||||
/// \return whether or not the head was found
|
||||
bool getHeadPosition(glm::vec3& headPosition) const;
|
||||
|
||||
|
||||
/// Returns the position of the neck joint.
|
||||
/// \return whether or not the neck was found
|
||||
bool getNeckPosition(glm::vec3& neckPosition) const;
|
||||
|
||||
|
||||
/// Returns the rotation of the neck joint's parent from default orientation
|
||||
/// \return whether or not the neck was found
|
||||
bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const;
|
||||
|
||||
|
||||
/// Retrieve the positions of up to two eye meshes.
|
||||
/// \return whether or not both eye meshes were found
|
||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
@ -98,19 +94,14 @@ public:
|
|||
|
||||
void computeBoundingShape(const FBXGeometry& geometry);
|
||||
void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha);
|
||||
float getBoundingShapeRadius() const { return _boundingShape.getRadius(); }
|
||||
const CapsuleShape& getBoundingShape() const { return _boundingShape; }
|
||||
const glm::vec3 getBoundingShapeOffset() const { return _boundingShapeLocalOffset; }
|
||||
|
||||
void resetShapePositionsToDefaultPose(); // DEBUG method
|
||||
float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; }
|
||||
float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; }
|
||||
const glm::vec3 getBoundingCapsuleOffset() const { return _boundingCapsuleLocalOffset; }
|
||||
|
||||
bool hasSkeleton();
|
||||
|
||||
float getHeadClipDistance() const { return _headClipDistance; }
|
||||
|
||||
void setIsFirstPerson(bool value) { _isFirstPerson = value; }
|
||||
bool getIsFirstPerson() const { return _isFirstPerson; }
|
||||
|
||||
virtual void onInvalidate() override;
|
||||
|
||||
signals:
|
||||
|
@ -124,26 +115,14 @@ protected:
|
|||
/// \param jointIndex index of joint in model
|
||||
/// \param position position of joint in model-frame
|
||||
void applyHandPosition(int jointIndex, const glm::vec3& position);
|
||||
|
||||
|
||||
void applyPalmData(int jointIndex, PalmData& palm);
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
virtual void updateJointState(int index);
|
||||
|
||||
void maybeUpdateLeanRotation(const JointState& parentState, JointState& state);
|
||||
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
|
||||
void cauterizeHead();
|
||||
void initHeadBones();
|
||||
void invalidateHeadBones();
|
||||
|
||||
private:
|
||||
|
||||
void renderJointConstraints(gpu::Batch& batch, int jointIndex);
|
||||
void renderOrientationDirections(gpu::Batch& batch, int jointIndex,
|
||||
void renderOrientationDirections(gpu::Batch& batch, int jointIndex,
|
||||
glm::vec3 position, const glm::quat& orientation, float size);
|
||||
|
||||
|
||||
struct OrientationLineIDs {
|
||||
int _up;
|
||||
int _front;
|
||||
|
@ -158,18 +137,16 @@ private:
|
|||
void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation);
|
||||
|
||||
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
||||
|
||||
Avatar* _owningAvatar;
|
||||
|
||||
CapsuleShape _boundingShape;
|
||||
glm::vec3 _boundingShapeLocalOffset;
|
||||
glm::vec3 _boundingCapsuleLocalOffset;
|
||||
float _boundingCapsuleRadius;
|
||||
float _boundingCapsuleHeight;
|
||||
|
||||
glm::vec3 _defaultEyeModelPosition;
|
||||
|
||||
float _headClipDistance; // Near clip distance to use if no separate head model
|
||||
|
||||
bool _isFirstPerson;
|
||||
std::vector<int> _headBones;
|
||||
};
|
||||
|
||||
#endif // hifi_SkeletonModel_h
|
||||
|
|
|
@ -47,7 +47,6 @@ public:
|
|||
|
||||
bool isMuted() const { return _isMuted; }
|
||||
void setIsMuted(bool isMuted) { _isMuted = isMuted; }
|
||||
void toggleMute();
|
||||
|
||||
static float getEyeDeflection() { return _eyeDeflection.get(); }
|
||||
static void setEyeDeflection(float eyeDeflection);
|
||||
|
@ -57,6 +56,8 @@ signals:
|
|||
|
||||
public slots:
|
||||
virtual void setEnabled(bool enabled) = 0;
|
||||
void toggleMute();
|
||||
bool getMuted() { return _isMuted; }
|
||||
|
||||
protected:
|
||||
virtual ~FaceTracker() {};
|
||||
|
|
|
@ -11,16 +11,16 @@
|
|||
//
|
||||
|
||||
#include "OculusManager.h"
|
||||
#include <gpu/GPUConfig.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <QDesktopWidget>
|
||||
#include <QGuiApplication>
|
||||
#include <gpu/GPUConfig.h>
|
||||
#include <QScreen>
|
||||
#include <CursorManager.h>
|
||||
#include <QOpenGLTimerQuery>
|
||||
#include <QGLWidget>
|
||||
|
||||
#include <CursorManager.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <avatar/MyAvatar.h>
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "TV3DManager.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include "gpu/GLBackend.h"
|
||||
#include "Application.h"
|
||||
#include <RenderArgs.h>
|
||||
|
||||
#include "TV3DManager.h"
|
||||
#include "Application.h"
|
||||
#include "Menu.h"
|
||||
|
||||
int TV3DManager::_screenWidth = 1;
|
||||
|
@ -63,6 +64,7 @@ void TV3DManager::setFrustum(Camera& whichCamera) {
|
|||
}
|
||||
|
||||
void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int screenHeight) {
|
||||
#ifdef THIS_CURRENTLY_BROKEN_WAITING_FOR_DISPLAY_PLUGINS
|
||||
if (screenHeight == 0) {
|
||||
screenHeight = 1; // prevent divide by 0
|
||||
}
|
||||
|
@ -72,6 +74,7 @@ void TV3DManager::configureCamera(Camera& whichCamera, int screenWidth, int scre
|
|||
setFrustum(whichCamera);
|
||||
|
||||
glViewport (0, 0, _screenWidth, _screenHeight); // sets drawing viewport
|
||||
#endif
|
||||
}
|
||||
|
||||
void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <glm/glm.hpp>
|
||||
|
||||
class Camera;
|
||||
class RenderArgs;
|
||||
|
||||
struct eyeFrustum {
|
||||
double left;
|
||||
|
|
|
@ -57,7 +57,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer<NLPacket> packet, Share
|
|||
|
||||
if (piggybackBytes) {
|
||||
// construct a new packet from the piggybacked one
|
||||
std::unique_ptr<char> buffer = std::unique_ptr<char>(new char[piggybackBytes]);
|
||||
auto buffer = std::unique_ptr<char[]>(new char[piggybackBytes]);
|
||||
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggybackBytes);
|
||||
|
||||
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggybackBytes, packet->getSenderSockAddr());
|
||||
|
|
26
interface/src/scripting/DialogsManagerScriptingInterface.cpp
Normal file
26
interface/src/scripting/DialogsManagerScriptingInterface.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// DialogsManagerScriptingInterface.cpp
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Zander Otavka on 7/17/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "DialogsManagerScriptingInterface.h"
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "ui/DialogsManager.h"
|
||||
|
||||
DialogsManagerScriptingInterface::DialogsManagerScriptingInterface() {
|
||||
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::addressBarToggled,
|
||||
this, &DialogsManagerScriptingInterface::addressBarToggled);
|
||||
}
|
||||
|
||||
void DialogsManagerScriptingInterface::toggleAddressBar() {
|
||||
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
|
||||
"toggleAddressBar", Qt::QueuedConnection);
|
||||
}
|
29
interface/src/scripting/DialogsManagerScriptingInterface.h
Normal file
29
interface/src/scripting/DialogsManagerScriptingInterface.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// DialogsManagerScriptingInterface.h
|
||||
// interface/src/scripting
|
||||
//
|
||||
// Created by Zander Otavka on 7/17/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DialogsManagerScriptInterface_h
|
||||
#define hifi_DialogsManagerScriptInterface_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class DialogsManagerScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DialogsManagerScriptingInterface();
|
||||
|
||||
public slots:
|
||||
void toggleAddressBar();
|
||||
|
||||
signals:
|
||||
void addressBarToggled();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -156,7 +156,7 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo
|
|||
buttons->addWidget(remove);
|
||||
connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle()));
|
||||
|
||||
_stop->connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool)));
|
||||
_stop->connect(_handle.get(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool)));
|
||||
_stop->setEnabled(_handle->isRunning());
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
void DialogsManager::toggleAddressBar() {
|
||||
AddressBarDialog::toggle();
|
||||
emit addressBarToggled();
|
||||
}
|
||||
|
||||
void DialogsManager::toggleDiskCacheEditor() {
|
||||
|
|
|
@ -72,6 +72,9 @@ public slots:
|
|||
// Application Update
|
||||
void showUpdateDialog();
|
||||
|
||||
signals:
|
||||
void addressBarToggled();
|
||||
|
||||
private slots:
|
||||
void toggleToolWindow();
|
||||
void hmdToolsClosed();
|
||||
|
|
|
@ -13,19 +13,25 @@
|
|||
|
||||
#include <QScriptValue>
|
||||
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <gpu/Batch.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "GeometryUtil.h"
|
||||
|
||||
|
||||
QString const BillboardOverlay::TYPE = "billboard";
|
||||
|
||||
BillboardOverlay::BillboardOverlay() {
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) :
|
||||
Planar3DOverlay(billboardOverlay),
|
||||
PanelAttachable(billboardOverlay),
|
||||
_url(billboardOverlay->_url),
|
||||
_texture(billboardOverlay->_texture),
|
||||
_fromImage(billboardOverlay->_fromImage),
|
||||
|
@ -33,6 +39,19 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) :
|
|||
{
|
||||
}
|
||||
|
||||
void BillboardOverlay::setTransforms(Transform& transform) {
|
||||
PanelAttachable::setTransforms(transform);
|
||||
if (_isFacingAvatar) {
|
||||
glm::quat rotation = Application::getInstance()->getCamera()->getOrientation();
|
||||
rotation *= glm::angleAxis(glm::pi<float>(), IDENTITY_UP);
|
||||
setRotation(rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void BillboardOverlay::update(float deltatime) {
|
||||
setTransforms(_transform);
|
||||
}
|
||||
|
||||
void BillboardOverlay::render(RenderArgs* args) {
|
||||
if (!_texture) {
|
||||
_isLoaded = true;
|
||||
|
@ -43,15 +62,8 @@ void BillboardOverlay::render(RenderArgs* args) {
|
|||
return;
|
||||
}
|
||||
|
||||
glm::quat rotation;
|
||||
if (_isFacingAvatar) {
|
||||
// rotate about vertical to face the camera
|
||||
rotation = args->_viewFrustum->getOrientation();
|
||||
rotation *= glm::angleAxis(glm::pi<float>(), IDENTITY_UP);
|
||||
rotation *= getRotation();
|
||||
} else {
|
||||
rotation = getRotation();
|
||||
}
|
||||
Q_ASSERT(args->_batch);
|
||||
auto batch = args->_batch;
|
||||
|
||||
float imageWidth = _texture->getWidth();
|
||||
float imageHeight = _texture->getHeight();
|
||||
|
@ -86,25 +98,25 @@ void BillboardOverlay::render(RenderArgs* args) {
|
|||
xColor color = getColor();
|
||||
float alpha = getAlpha();
|
||||
|
||||
auto batch = args->_batch;
|
||||
setTransforms(_transform);
|
||||
Transform transform = _transform;
|
||||
transform.postScale(glm::vec3(getDimensions(), 1.0f));
|
||||
|
||||
if (batch) {
|
||||
Transform transform = _transform;
|
||||
transform.postScale(glm::vec3(getDimensions(), 1.0f));
|
||||
transform.setRotation(rotation);
|
||||
|
||||
batch->setModelTransform(transform);
|
||||
batch->setResourceTexture(0, _texture->getGPUTexture());
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
|
||||
batch->setModelTransform(transform);
|
||||
batch->setResourceTexture(0, _texture->getGPUTexture());
|
||||
|
||||
batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me
|
||||
}
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch, true, true, false, true);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(
|
||||
*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)
|
||||
);
|
||||
|
||||
batch->setResourceTexture(0, args->_whiteTexture); // restore default white color after me
|
||||
}
|
||||
|
||||
void BillboardOverlay::setProperties(const QScriptValue &properties) {
|
||||
Planar3DOverlay::setProperties(properties);
|
||||
PanelAttachable::setProperties(properties);
|
||||
|
||||
QScriptValue urlValue = properties.property("url");
|
||||
if (urlValue.isValid()) {
|
||||
|
@ -161,7 +173,14 @@ QScriptValue BillboardOverlay::getProperty(const QString& property) {
|
|||
if (property == "isFacingAvatar") {
|
||||
return _isFacingAvatar;
|
||||
}
|
||||
if (property == "offsetPosition") {
|
||||
return vec3toScriptValue(_scriptEngine, getOffsetPosition());
|
||||
}
|
||||
|
||||
QScriptValue value = PanelAttachable::getProperty(_scriptEngine, property);
|
||||
if (value.isValid()) {
|
||||
return value;
|
||||
}
|
||||
return Planar3DOverlay::getProperty(property);
|
||||
}
|
||||
|
||||
|
@ -175,15 +194,10 @@ void BillboardOverlay::setBillboardURL(const QString& url) {
|
|||
}
|
||||
|
||||
bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float& distance, BoxFace& face) {
|
||||
|
||||
float& distance, BoxFace& face) {
|
||||
if (_texture && _texture->isLoaded()) {
|
||||
glm::quat rotation = getRotation();
|
||||
if (_isFacingAvatar) {
|
||||
// rotate about vertical to face the camera
|
||||
rotation = Application::getInstance()->getCamera()->getRotation();
|
||||
rotation *= glm::angleAxis(glm::pi<float>(), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
}
|
||||
// Make sure position and rotation is updated.
|
||||
setTransforms(_transform);
|
||||
|
||||
// Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale.
|
||||
bool isNull = _fromImage.isNull();
|
||||
|
@ -192,7 +206,7 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v
|
|||
float maxSize = glm::max(width, height);
|
||||
glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize);
|
||||
|
||||
return findRayRectangleIntersection(origin, direction, rotation, getPosition(), dimensions, distance);
|
||||
return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -15,15 +15,21 @@
|
|||
#include <TextureCache.h>
|
||||
|
||||
#include "Planar3DOverlay.h"
|
||||
#include "PanelAttachable.h"
|
||||
|
||||
class BillboardOverlay : public Planar3DOverlay {
|
||||
class BillboardOverlay : public Planar3DOverlay, public PanelAttachable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
BillboardOverlay();
|
||||
BillboardOverlay(const BillboardOverlay* billboardOverlay);
|
||||
|
||||
virtual void render(RenderArgs* args);
|
||||
|
||||
virtual void update(float deltatime);
|
||||
|
||||
// setters
|
||||
void setURL(const QString& url);
|
||||
void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; }
|
||||
|
@ -36,9 +42,12 @@ public:
|
|||
|
||||
virtual BillboardOverlay* createClone() const;
|
||||
|
||||
protected:
|
||||
virtual void setTransforms(Transform& transform);
|
||||
|
||||
private:
|
||||
void setBillboardURL(const QString& url);
|
||||
|
||||
|
||||
QString _url;
|
||||
NetworkTexturePointer _texture;
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
|
||||
QString const Circle3DOverlay::TYPE = "circle3d";
|
||||
|
||||
Circle3DOverlay::Circle3DOverlay() :
|
||||
_startAt(0.0f),
|
||||
_endAt(360.0f),
|
||||
|
@ -119,19 +121,21 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
float angle = startAt;
|
||||
float angleInRadians = glm::radians(angle);
|
||||
glm::vec2 firstInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
||||
glm::vec2 firstOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
|
||||
points << firstInnerPoint << firstOuterPoint;
|
||||
glm::vec2 mostRecentInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
||||
glm::vec2 mostRecentOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
|
||||
while (angle < endAt) {
|
||||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 thisInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
||||
glm::vec2 thisOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
|
||||
points << thisOuterPoint << thisInnerPoint;
|
||||
points << mostRecentInnerPoint << mostRecentOuterPoint << thisOuterPoint; // first triangle
|
||||
points << mostRecentInnerPoint << thisInnerPoint << thisOuterPoint; // second triangle
|
||||
|
||||
angle += SLICE_ANGLE;
|
||||
|
||||
mostRecentInnerPoint = thisInnerPoint;
|
||||
mostRecentOuterPoint = thisOuterPoint;
|
||||
}
|
||||
|
||||
// get the last slice portion....
|
||||
|
@ -139,13 +143,14 @@ void Circle3DOverlay::render(RenderArgs* args) {
|
|||
angleInRadians = glm::radians(angle);
|
||||
glm::vec2 lastInnerPoint(cosf(angleInRadians) * innerRadius, sinf(angleInRadians) * innerRadius);
|
||||
glm::vec2 lastOuterPoint(cosf(angleInRadians) * outerRadius, sinf(angleInRadians) * outerRadius);
|
||||
|
||||
points << lastOuterPoint << lastInnerPoint;
|
||||
|
||||
points << mostRecentInnerPoint << mostRecentOuterPoint << lastOuterPoint; // first triangle
|
||||
points << mostRecentInnerPoint << lastInnerPoint << lastOuterPoint; // second triangle
|
||||
|
||||
geometryCache->updateVertices(_quadVerticesID, points, color);
|
||||
}
|
||||
|
||||
geometryCache->renderVertices(batch, gpu::QUAD_STRIP, _quadVerticesID);
|
||||
geometryCache->renderVertices(batch, gpu::TRIANGLES, _quadVerticesID);
|
||||
|
||||
} else {
|
||||
if (_lineVerticesID == GeometryCache::UNKNOWN_ID) {
|
||||
|
|
|
@ -18,6 +18,9 @@ class Circle3DOverlay : public Planar3DOverlay {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
Circle3DOverlay();
|
||||
Circle3DOverlay(const Circle3DOverlay* circle3DOverlay);
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <GeometryCache.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
QString const Cube3DOverlay::TYPE = "cube";
|
||||
|
||||
Cube3DOverlay::Cube3DOverlay(const Cube3DOverlay* cube3DOverlay) :
|
||||
Volume3DOverlay(cube3DOverlay)
|
||||
{
|
||||
|
|
|
@ -17,6 +17,9 @@ class Cube3DOverlay : public Volume3DOverlay {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
Cube3DOverlay() {}
|
||||
Cube3DOverlay(const Cube3DOverlay* cube3DOverlay);
|
||||
|
||||
|
|
197
interface/src/ui/overlays/FloatingUIPanel.cpp
Normal file
197
interface/src/ui/overlays/FloatingUIPanel.cpp
Normal file
|
@ -0,0 +1,197 @@
|
|||
//
|
||||
// FloatingUIPanel.cpp
|
||||
// interface/src/ui/overlays
|
||||
//
|
||||
// Created by Zander Otavka on 7/2/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "FloatingUIPanel.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "avatar/AvatarManager.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "Application.h"
|
||||
#include "Base3DOverlay.h"
|
||||
|
||||
std::function<glm::vec3()> const FloatingUIPanel::AVATAR_POSITION = []() -> glm::vec3 {
|
||||
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition();
|
||||
};
|
||||
|
||||
std::function<glm::quat()> const FloatingUIPanel::AVATAR_ORIENTATION = []() -> glm::quat {
|
||||
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getOrientation() *
|
||||
glm::angleAxis(glm::pi<float>(), IDENTITY_UP);
|
||||
};
|
||||
|
||||
glm::vec3 FloatingUIPanel::getPosition() const {
|
||||
return getOffsetRotation() * getOffsetPosition() + getAnchorPosition();
|
||||
}
|
||||
|
||||
glm::quat FloatingUIPanel::getRotation() const {
|
||||
return getOffsetRotation() * getFacingRotation();
|
||||
}
|
||||
|
||||
void FloatingUIPanel::setAnchorPosition(const glm::vec3& position) {
|
||||
setAnchorPosition([position]() -> glm::vec3 {
|
||||
return position;
|
||||
});
|
||||
}
|
||||
|
||||
void FloatingUIPanel::setOffsetRotation(const glm::quat& rotation) {
|
||||
setOffsetRotation([rotation]() -> glm::quat {
|
||||
return rotation;
|
||||
});
|
||||
}
|
||||
|
||||
void FloatingUIPanel::addChild(unsigned int childId) {
|
||||
if (!_children.contains(childId)) {
|
||||
_children.append(childId);
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingUIPanel::removeChild(unsigned int childId) {
|
||||
if (_children.contains(childId)) {
|
||||
_children.removeOne(childId);
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue FloatingUIPanel::getProperty(const QString &property) {
|
||||
if (property == "anchorPosition") {
|
||||
return vec3toScriptValue(_scriptEngine, getAnchorPosition());
|
||||
}
|
||||
if (property == "offsetRotation") {
|
||||
return quatToScriptValue(_scriptEngine, getOffsetRotation());
|
||||
}
|
||||
if (property == "offsetPosition") {
|
||||
return vec3toScriptValue(_scriptEngine, getOffsetPosition());
|
||||
}
|
||||
if (property == "facingRotation") {
|
||||
return quatToScriptValue(_scriptEngine, getFacingRotation());
|
||||
}
|
||||
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
void FloatingUIPanel::setProperties(const QScriptValue &properties) {
|
||||
QScriptValue anchor = properties.property("anchorPosition");
|
||||
if (anchor.isValid()) {
|
||||
QScriptValue bindType = anchor.property("bind");
|
||||
QScriptValue value = anchor.property("value");
|
||||
|
||||
if (bindType.isValid()) {
|
||||
QString bindTypeString = bindType.toVariant().toString();
|
||||
if (bindTypeString == "myAvatar") {
|
||||
setAnchorPosition(AVATAR_POSITION);
|
||||
} else if (value.isValid()) {
|
||||
if (bindTypeString == "overlay") {
|
||||
Overlay::Pointer overlay = Application::getInstance()->getOverlays()
|
||||
.getOverlay(value.toVariant().toUInt());
|
||||
if (overlay->is3D()) {
|
||||
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(overlay);
|
||||
setAnchorPosition([&overlay3D]() -> glm::vec3 {
|
||||
return overlay3D->getPosition();
|
||||
});
|
||||
}
|
||||
} else if (bindTypeString == "panel") {
|
||||
FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays()
|
||||
.getPanel(value.toVariant().toUInt());
|
||||
setAnchorPosition([panel]() -> glm::vec3 {
|
||||
return panel->getPosition();
|
||||
});
|
||||
} else if (bindTypeString == "vec3") {
|
||||
QScriptValue x = value.property("x");
|
||||
QScriptValue y = value.property("y");
|
||||
QScriptValue z = value.property("z");
|
||||
if (x.isValid() && y.isValid() && z.isValid()) {
|
||||
glm::vec3 newPosition;
|
||||
newPosition.x = x.toVariant().toFloat();
|
||||
newPosition.y = y.toVariant().toFloat();
|
||||
newPosition.z = z.toVariant().toFloat();
|
||||
setAnchorPosition(newPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue offsetRotation = properties.property("offsetRotation");
|
||||
if (offsetRotation.isValid()) {
|
||||
QScriptValue bindType = offsetRotation.property("bind");
|
||||
QScriptValue value = offsetRotation.property("value");
|
||||
|
||||
if (bindType.isValid()) {
|
||||
QString bindTypeString = bindType.toVariant().toString();
|
||||
if (bindTypeString == "myAvatar") {
|
||||
setOffsetRotation(AVATAR_ORIENTATION);
|
||||
} else if (value.isValid()) {
|
||||
if (bindTypeString == "overlay") {
|
||||
Overlay::Pointer overlay = Application::getInstance()->getOverlays()
|
||||
.getOverlay(value.toVariant().toUInt());
|
||||
if (overlay->is3D()) {
|
||||
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(overlay);
|
||||
setOffsetRotation([&overlay3D]() -> glm::quat {
|
||||
return overlay3D->getRotation();
|
||||
});
|
||||
}
|
||||
} else if (bindTypeString == "panel") {
|
||||
FloatingUIPanel::Pointer panel = Application::getInstance()->getOverlays()
|
||||
.getPanel(value.toVariant().toUInt());
|
||||
setOffsetRotation([panel]() -> glm::quat {
|
||||
return panel->getRotation();
|
||||
});
|
||||
} else if (bindTypeString == "quat") {
|
||||
QScriptValue x = value.property("x");
|
||||
QScriptValue y = value.property("y");
|
||||
QScriptValue z = value.property("z");
|
||||
QScriptValue w = value.property("w");
|
||||
|
||||
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
|
||||
glm::quat newRotation;
|
||||
newRotation.x = x.toVariant().toFloat();
|
||||
newRotation.y = y.toVariant().toFloat();
|
||||
newRotation.z = z.toVariant().toFloat();
|
||||
newRotation.w = w.toVariant().toFloat();
|
||||
setOffsetRotation(newRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue offsetPosition = properties.property("offsetPosition");
|
||||
if (offsetPosition.isValid()) {
|
||||
QScriptValue x = offsetPosition.property("x");
|
||||
QScriptValue y = offsetPosition.property("y");
|
||||
QScriptValue z = offsetPosition.property("z");
|
||||
if (x.isValid() && y.isValid() && z.isValid()) {
|
||||
glm::vec3 newPosition;
|
||||
newPosition.x = x.toVariant().toFloat();
|
||||
newPosition.y = y.toVariant().toFloat();
|
||||
newPosition.z = z.toVariant().toFloat();
|
||||
setOffsetPosition(newPosition);
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue facingRotation = properties.property("facingRotation");
|
||||
if (facingRotation.isValid()) {
|
||||
QScriptValue x = facingRotation.property("x");
|
||||
QScriptValue y = facingRotation.property("y");
|
||||
QScriptValue z = facingRotation.property("z");
|
||||
QScriptValue w = facingRotation.property("w");
|
||||
|
||||
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
|
||||
glm::quat newRotation;
|
||||
newRotation.x = x.toVariant().toFloat();
|
||||
newRotation.y = y.toVariant().toFloat();
|
||||
newRotation.z = z.toVariant().toFloat();
|
||||
newRotation.w = w.toVariant().toFloat();
|
||||
setFacingRotation(newRotation);
|
||||
}
|
||||
}
|
||||
}
|
63
interface/src/ui/overlays/FloatingUIPanel.h
Normal file
63
interface/src/ui/overlays/FloatingUIPanel.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// FloatingUIPanel.h
|
||||
// interface/src/ui/overlays
|
||||
//
|
||||
// Created by Zander Otavka on 7/2/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_FloatingUIPanel_h
|
||||
#define hifi_FloatingUIPanel_h
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <QScriptValue>
|
||||
|
||||
class FloatingUIPanel : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef std::shared_ptr<FloatingUIPanel> Pointer;
|
||||
|
||||
void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; }
|
||||
|
||||
glm::vec3 getAnchorPosition() const { return _anchorPosition(); }
|
||||
glm::quat getOffsetRotation() const { return _offsetRotation(); }
|
||||
glm::vec3 getOffsetPosition() const { return _offsetPosition; }
|
||||
glm::quat getFacingRotation() const { return _facingRotation; }
|
||||
glm::vec3 getPosition() const;
|
||||
glm::quat getRotation() const;
|
||||
|
||||
void setAnchorPosition(const std::function<glm::vec3()>& func) { _anchorPosition = func; }
|
||||
void setAnchorPosition(const glm::vec3& position);
|
||||
void setOffsetRotation(const std::function<glm::quat()>& func) { _offsetRotation = func; }
|
||||
void setOffsetRotation(const glm::quat& rotation);
|
||||
void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; }
|
||||
void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; }
|
||||
|
||||
const QList<unsigned int>& getChildren() { return _children; }
|
||||
void addChild(unsigned int childId);
|
||||
void removeChild(unsigned int childId);
|
||||
unsigned int popLastChild() { return _children.takeLast(); }
|
||||
|
||||
QScriptValue getProperty(const QString& property);
|
||||
void setProperties(const QScriptValue& properties);
|
||||
|
||||
private:
|
||||
static std::function<glm::vec3()> const AVATAR_POSITION;
|
||||
static std::function<glm::quat()> const AVATAR_ORIENTATION;
|
||||
|
||||
std::function<glm::vec3()> _anchorPosition{AVATAR_POSITION};
|
||||
std::function<glm::quat()> _offsetRotation{AVATAR_ORIENTATION};
|
||||
glm::vec3 _offsetPosition{0, 0, 0};
|
||||
glm::quat _facingRotation{1, 0, 0, 0};
|
||||
QScriptEngine* _scriptEngine;
|
||||
QList<unsigned int> _children;
|
||||
};
|
||||
|
||||
#endif // hifi_FloatingUIPanel_h
|
|
@ -18,6 +18,9 @@
|
|||
#include <PathUtils.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
|
||||
QString const Grid3DOverlay::TYPE = "grid";
|
||||
|
||||
Grid3DOverlay::Grid3DOverlay() :
|
||||
_minorGridWidth(1.0),
|
||||
_majorGridEvery(5) {
|
||||
|
|
|
@ -18,6 +18,9 @@ class Grid3DOverlay : public Planar3DOverlay {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
Grid3DOverlay();
|
||||
Grid3DOverlay(const Grid3DOverlay* grid3DOverlay);
|
||||
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
#include <gpu/StandardShaderLib.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
|
||||
QString const ImageOverlay::TYPE = "image";
|
||||
|
||||
ImageOverlay::ImageOverlay() :
|
||||
_imageURL(),
|
||||
_renderImage(false),
|
||||
|
|
|
@ -24,6 +24,9 @@ class ImageOverlay : public Overlay2D {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
ImageOverlay();
|
||||
ImageOverlay(const ImageOverlay* imageOverlay);
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
#include <GeometryCache.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
|
||||
QString const Line3DOverlay::TYPE = "line3d";
|
||||
|
||||
Line3DOverlay::Line3DOverlay() :
|
||||
_geometryCacheID(DependencyManager::get<GeometryCache>()->allocateID())
|
||||
{
|
||||
|
|
|
@ -17,6 +17,9 @@ class Line3DOverlay : public Base3DOverlay {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
Line3DOverlay();
|
||||
Line3DOverlay(const Line3DOverlay* line3DOverlay);
|
||||
~Line3DOverlay();
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#include <EntityTreeRenderer.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
||||
|
||||
QString const LocalModelsOverlay::TYPE = "localmodels";
|
||||
|
||||
LocalModelsOverlay::LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer) :
|
||||
Volume3DOverlay(),
|
||||
_entityTreeRenderer(entityTreeRenderer) {
|
||||
|
|
|
@ -19,6 +19,9 @@ class EntityTreeRenderer;
|
|||
class LocalModelsOverlay : public Volume3DOverlay {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
LocalModelsOverlay(EntityTreeRenderer* entityTreeRenderer);
|
||||
LocalModelsOverlay(const LocalModelsOverlay* localModelsOverlay);
|
||||
|
||||
|
|
|
@ -13,8 +13,11 @@
|
|||
|
||||
#include "Application.h"
|
||||
|
||||
|
||||
QString const ModelOverlay::TYPE = "model";
|
||||
|
||||
ModelOverlay::ModelOverlay()
|
||||
: _model(),
|
||||
: _model(nullptr),
|
||||
_modelTextures(QVariantMap()),
|
||||
_updateModel(false)
|
||||
{
|
||||
|
@ -24,7 +27,7 @@ ModelOverlay::ModelOverlay()
|
|||
|
||||
ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) :
|
||||
Volume3DOverlay(modelOverlay),
|
||||
_model(),
|
||||
_model(nullptr),
|
||||
_modelTextures(QVariantMap()),
|
||||
_url(modelOverlay->_url),
|
||||
_updateModel(false)
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
class ModelOverlay : public Volume3DOverlay {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
ModelOverlay();
|
||||
ModelOverlay(const ModelOverlay* modelOverlay);
|
||||
|
||||
|
|
|
@ -110,7 +110,8 @@ void Overlay::setProperties(const QScriptValue& properties) {
|
|||
}
|
||||
|
||||
if (properties.property("visible").isValid()) {
|
||||
setVisible(properties.property("visible").toVariant().toBool());
|
||||
bool visible = properties.property("visible").toVariant().toBool();
|
||||
setVisible(visible);
|
||||
}
|
||||
|
||||
if (properties.property("anchor").isValid()) {
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
|
||||
// getters
|
||||
virtual QString getType() const = 0;
|
||||
virtual bool is3D() const = 0;
|
||||
bool isLoaded() { return _isLoaded; }
|
||||
bool getVisible() const { return _visible; }
|
||||
|
|
|
@ -48,6 +48,7 @@ Overlays::~Overlays() {
|
|||
}
|
||||
_overlaysHUD.clear();
|
||||
_overlaysWorld.clear();
|
||||
_panels.clear();
|
||||
}
|
||||
|
||||
cleanupOverlaysToDelete();
|
||||
|
@ -124,85 +125,84 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) {
|
||||
unsigned int thisID = 0;
|
||||
Overlay* thisOverlay = NULL;
|
||||
|
||||
bool created = true;
|
||||
if (type == "image") {
|
||||
thisOverlay = new ImageOverlay();
|
||||
} else if (type == "text") {
|
||||
thisOverlay = new TextOverlay();
|
||||
} else if (type == "text3d") {
|
||||
thisOverlay = new Text3DOverlay();
|
||||
} else if (type == "cube") {
|
||||
thisOverlay = new Cube3DOverlay();
|
||||
} else if (type == "sphere") {
|
||||
thisOverlay = new Sphere3DOverlay();
|
||||
} else if (type == "circle3d") {
|
||||
thisOverlay = new Circle3DOverlay();
|
||||
} else if (type == "rectangle3d") {
|
||||
thisOverlay = new Rectangle3DOverlay();
|
||||
} else if (type == "line3d") {
|
||||
thisOverlay = new Line3DOverlay();
|
||||
} else if (type == "grid") {
|
||||
thisOverlay = new Grid3DOverlay();
|
||||
} else if (type == "localmodels") {
|
||||
thisOverlay = new LocalModelsOverlay(Application::getInstance()->getEntityClipboardRenderer());
|
||||
} else if (type == "model") {
|
||||
thisOverlay = new ModelOverlay();
|
||||
} else if (type == "billboard") {
|
||||
thisOverlay = new BillboardOverlay();
|
||||
} else {
|
||||
created = false;
|
||||
Overlay::Pointer Overlays::getOverlay(unsigned int id) const {
|
||||
if (_overlaysHUD.contains(id)) {
|
||||
return _overlaysHUD[id];
|
||||
}
|
||||
|
||||
if (created) {
|
||||
thisOverlay->setProperties(properties);
|
||||
thisID = addOverlay(thisOverlay);
|
||||
if (_overlaysWorld.contains(id)) {
|
||||
return _overlaysWorld[id];
|
||||
}
|
||||
|
||||
return thisID;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned int Overlays::addOverlay(Overlay* overlay) {
|
||||
Overlay::Pointer overlayPointer(overlay);
|
||||
unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) {
|
||||
Overlay::Pointer thisOverlay = nullptr;
|
||||
|
||||
if (type == ImageOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<ImageOverlay>();
|
||||
} else if (type == TextOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<TextOverlay>();
|
||||
} else if (type == Text3DOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<Text3DOverlay>();
|
||||
} else if (type == Cube3DOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<Cube3DOverlay>();
|
||||
} else if (type == Sphere3DOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<Sphere3DOverlay>();
|
||||
} else if (type == Circle3DOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<Circle3DOverlay>();
|
||||
} else if (type == Rectangle3DOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<Rectangle3DOverlay>();
|
||||
} else if (type == Line3DOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<Line3DOverlay>();
|
||||
} else if (type == Grid3DOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<Grid3DOverlay>();
|
||||
} else if (type == LocalModelsOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<LocalModelsOverlay>(Application::getInstance()->getEntityClipboardRenderer());
|
||||
} else if (type == ModelOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<ModelOverlay>();
|
||||
} else if (type == BillboardOverlay::TYPE) {
|
||||
thisOverlay = std::make_shared<BillboardOverlay>();
|
||||
}
|
||||
|
||||
if (thisOverlay) {
|
||||
thisOverlay->setProperties(properties);
|
||||
return addOverlay(thisOverlay);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int Overlays::addOverlay(Overlay::Pointer overlay) {
|
||||
overlay->init(_scriptEngine);
|
||||
|
||||
QWriteLocker lock(&_lock);
|
||||
unsigned int thisID = _nextOverlayID;
|
||||
_nextOverlayID++;
|
||||
if (overlay->is3D()) {
|
||||
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(overlay);
|
||||
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(overlay);
|
||||
if (overlay3D->getDrawOnHUD()) {
|
||||
_overlaysHUD[thisID] = overlayPointer;
|
||||
_overlaysHUD[thisID] = overlay;
|
||||
} else {
|
||||
_overlaysWorld[thisID] = overlayPointer;
|
||||
_overlaysWorld[thisID] = overlay;
|
||||
|
||||
render::ScenePointer scene = Application::getInstance()->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
|
||||
overlayPointer->addToScene(overlayPointer, scene, pendingChanges);
|
||||
overlay->addToScene(overlay, scene, pendingChanges);
|
||||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
} else {
|
||||
_overlaysHUD[thisID] = overlayPointer;
|
||||
_overlaysHUD[thisID] = overlay;
|
||||
}
|
||||
|
||||
|
||||
return thisID;
|
||||
}
|
||||
|
||||
unsigned int Overlays::cloneOverlay(unsigned int id) {
|
||||
Overlay::Pointer thisOverlay = NULL;
|
||||
if (_overlaysHUD.contains(id)) {
|
||||
thisOverlay = _overlaysHUD[id];
|
||||
} else if (_overlaysWorld.contains(id)) {
|
||||
thisOverlay = _overlaysWorld[id];
|
||||
}
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
|
||||
if (thisOverlay) {
|
||||
return addOverlay(thisOverlay->createClone());
|
||||
return addOverlay(Overlay::Pointer(thisOverlay->createClone()));
|
||||
}
|
||||
|
||||
return 0; // Not found
|
||||
|
@ -210,14 +210,8 @@ unsigned int Overlays::cloneOverlay(unsigned int id) {
|
|||
|
||||
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
|
||||
QWriteLocker lock(&_lock);
|
||||
Overlay::Pointer thisOverlay;
|
||||
|
||||
if (_overlaysHUD.contains(id)) {
|
||||
thisOverlay = _overlaysHUD[id];
|
||||
} else if (_overlaysWorld.contains(id)) {
|
||||
thisOverlay = _overlaysWorld[id];
|
||||
}
|
||||
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
if (thisOverlay) {
|
||||
if (thisOverlay->is3D()) {
|
||||
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(thisOverlay);
|
||||
|
@ -258,8 +252,51 @@ void Overlays::deleteOverlay(unsigned int id) {
|
|||
}
|
||||
}
|
||||
|
||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlayToDelete);
|
||||
if (attachable && attachable->getAttachedPanel()) {
|
||||
attachable->getAttachedPanel()->removeChild(id);
|
||||
attachable->setAttachedPanel(nullptr);
|
||||
}
|
||||
|
||||
QWriteLocker lock(&_deleteLock);
|
||||
_overlaysToDelete.push_back(overlayToDelete);
|
||||
|
||||
emit overlayDeleted(id);
|
||||
}
|
||||
|
||||
QString Overlays::getOverlayType(unsigned int overlayId) const {
|
||||
Overlay::Pointer overlay = getOverlay(overlayId);
|
||||
if (overlay) {
|
||||
return overlay->getType();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
unsigned int Overlays::getAttachedPanel(unsigned int childId) const {
|
||||
Overlay::Pointer overlay = getOverlay(childId);
|
||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
|
||||
if (attachable) {
|
||||
return _panels.key(attachable->getAttachedPanel());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Overlays::setAttachedPanel(unsigned int childId, unsigned int panelId) {
|
||||
Overlay::Pointer overlay = getOverlay(childId);
|
||||
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
|
||||
if (attachable) {
|
||||
if (_panels.contains(panelId)) {
|
||||
auto panel = _panels[panelId];
|
||||
panel->addChild(childId);
|
||||
attachable->setAttachedPanel(panel);
|
||||
} else {
|
||||
auto panel = attachable->getAttachedPanel();
|
||||
if (panel) {
|
||||
panel->removeChild(childId);
|
||||
attachable->setAttachedPanel(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
||||
|
@ -302,13 +339,8 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
|||
|
||||
OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& property) {
|
||||
OverlayPropertyResult result;
|
||||
Overlay::Pointer thisOverlay;
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
QReadLocker lock(&_lock);
|
||||
if (_overlaysHUD.contains(id)) {
|
||||
thisOverlay = _overlaysHUD[id];
|
||||
} else if (_overlaysWorld.contains(id)) {
|
||||
thisOverlay = _overlaysWorld[id];
|
||||
}
|
||||
if (thisOverlay) {
|
||||
result.value = thisOverlay->getProperty(property);
|
||||
}
|
||||
|
@ -456,12 +488,8 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
|
|||
|
||||
bool Overlays::isLoaded(unsigned int id) {
|
||||
QReadLocker lock(&_lock);
|
||||
Overlay::Pointer thisOverlay = NULL;
|
||||
if (_overlaysHUD.contains(id)) {
|
||||
thisOverlay = _overlaysHUD[id];
|
||||
} else if (_overlaysWorld.contains(id)) {
|
||||
thisOverlay = _overlaysWorld[id];
|
||||
} else {
|
||||
Overlay::Pointer thisOverlay = getOverlay(id);
|
||||
if (!thisOverlay) {
|
||||
return false; // not found
|
||||
}
|
||||
return thisOverlay->isLoaded();
|
||||
|
@ -483,3 +511,56 @@ QSizeF Overlays::textSize(unsigned int id, const QString& text) const {
|
|||
}
|
||||
return QSizeF(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
unsigned int Overlays::addPanel(FloatingUIPanel::Pointer panel) {
|
||||
QWriteLocker lock(&_lock);
|
||||
|
||||
unsigned int thisID = _nextOverlayID;
|
||||
_nextOverlayID++;
|
||||
_panels[thisID] = panel;
|
||||
|
||||
return thisID;
|
||||
}
|
||||
|
||||
unsigned int Overlays::addPanel(const QScriptValue& properties) {
|
||||
FloatingUIPanel::Pointer panel = std::make_shared<FloatingUIPanel>();
|
||||
panel->init(_scriptEngine);
|
||||
panel->setProperties(properties);
|
||||
return addPanel(panel);
|
||||
}
|
||||
|
||||
void Overlays::editPanel(unsigned int panelId, const QScriptValue& properties) {
|
||||
if (_panels.contains(panelId)) {
|
||||
_panels[panelId]->setProperties(properties);
|
||||
}
|
||||
}
|
||||
|
||||
OverlayPropertyResult Overlays::getPanelProperty(unsigned int panelId, const QString& property) {
|
||||
OverlayPropertyResult result;
|
||||
if (_panels.contains(panelId)) {
|
||||
FloatingUIPanel::Pointer thisPanel = _panels[panelId];
|
||||
QReadLocker lock(&_lock);
|
||||
result.value = thisPanel->getProperty(property);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void Overlays::deletePanel(unsigned int panelId) {
|
||||
FloatingUIPanel::Pointer panelToDelete;
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_lock);
|
||||
if (_panels.contains(panelId)) {
|
||||
panelToDelete = _panels.take(panelId);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (!panelToDelete->getChildren().isEmpty()) {
|
||||
deleteOverlay(panelToDelete->popLastChild());
|
||||
}
|
||||
|
||||
emit panelDeleted(panelId);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,14 @@
|
|||
// Overlays.h
|
||||
// interface/src/ui/overlays
|
||||
//
|
||||
// Modified by Zander Otavka on 7/15/15
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Exposes methods for managing `Overlay`s and `FloatingUIPanel`s to scripts.
|
||||
//
|
||||
// YOU SHOULD NOT USE `Overlays` DIRECTLY, unless you like pain and deprecation. Instead, use the
|
||||
// object oriented abstraction layer found in `examples/libraries/overlayUtils.js`.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
@ -16,6 +22,9 @@
|
|||
|
||||
#include "Overlay.h"
|
||||
|
||||
#include "FloatingUIPanel.h"
|
||||
#include "PanelAttachable.h"
|
||||
|
||||
class PickRay;
|
||||
|
||||
class OverlayPropertyResult {
|
||||
|
@ -57,12 +66,16 @@ public:
|
|||
void update(float deltatime);
|
||||
void renderHUD(RenderArgs* renderArgs);
|
||||
|
||||
Overlay::Pointer getOverlay(unsigned int id) const;
|
||||
FloatingUIPanel::Pointer getPanel(unsigned int id) const { return _panels[id]; }
|
||||
|
||||
public slots:
|
||||
/// adds an overlay with the specific properties
|
||||
unsigned int addOverlay(const QString& type, const QScriptValue& properties);
|
||||
|
||||
/// adds an overlay that's already been created
|
||||
unsigned int addOverlay(Overlay* overlay);
|
||||
unsigned int addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
|
||||
unsigned int addOverlay(Overlay::Pointer overlay);
|
||||
|
||||
/// clones an existing overlay
|
||||
unsigned int cloneOverlay(unsigned int id);
|
||||
|
@ -74,6 +87,12 @@ public slots:
|
|||
/// deletes a particle
|
||||
void deleteOverlay(unsigned int id);
|
||||
|
||||
/// get the string type of the overlay used in addOverlay
|
||||
QString getOverlayType(unsigned int overlayId) const;
|
||||
|
||||
unsigned int getAttachedPanel(unsigned int childId) const;
|
||||
void setAttachedPanel(unsigned int childId, unsigned int panelId);
|
||||
|
||||
/// returns the top most 2D overlay at the screen point, or 0 if not overlay at that point
|
||||
unsigned int getOverlayAtPoint(const glm::vec2& point);
|
||||
|
||||
|
@ -90,12 +109,35 @@ public slots:
|
|||
/// overlay; in meters if it is a 3D text overlay
|
||||
QSizeF textSize(unsigned int id, const QString& text) const;
|
||||
|
||||
|
||||
/// adds a panel that has already been created
|
||||
unsigned int addPanel(FloatingUIPanel::Pointer panel);
|
||||
|
||||
/// creates and adds a panel based on a set of properties
|
||||
unsigned int addPanel(const QScriptValue& properties);
|
||||
|
||||
/// edit the properties of a panel
|
||||
void editPanel(unsigned int panelId, const QScriptValue& properties);
|
||||
|
||||
/// get a property of a panel
|
||||
OverlayPropertyResult getPanelProperty(unsigned int panelId, const QString& property);
|
||||
|
||||
/// deletes a panel and all child overlays
|
||||
void deletePanel(unsigned int panelId);
|
||||
|
||||
signals:
|
||||
void overlayDeleted(unsigned int id);
|
||||
void panelDeleted(unsigned int id);
|
||||
|
||||
private:
|
||||
void cleanupOverlaysToDelete();
|
||||
|
||||
QMap<unsigned int, Overlay::Pointer> _overlaysHUD;
|
||||
QMap<unsigned int, Overlay::Pointer> _overlaysWorld;
|
||||
QMap<unsigned int, FloatingUIPanel::Pointer> _panels;
|
||||
QList<Overlay::Pointer> _overlaysToDelete;
|
||||
unsigned int _nextOverlayID;
|
||||
|
||||
QReadWriteLock _lock;
|
||||
QReadWriteLock _deleteLock;
|
||||
QScriptEngine* _scriptEngine;
|
||||
|
|
80
interface/src/ui/overlays/PanelAttachable.cpp
Normal file
80
interface/src/ui/overlays/PanelAttachable.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// PanelAttachable.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Zander Otavka on 7/15/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "PanelAttachable.h"
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
PanelAttachable::PanelAttachable() :
|
||||
_attachedPanel(nullptr),
|
||||
_facingRotation(1, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
PanelAttachable::PanelAttachable(const PanelAttachable* panelAttachable) :
|
||||
_attachedPanel(panelAttachable->_attachedPanel),
|
||||
_offsetPosition(panelAttachable->_offsetPosition),
|
||||
_facingRotation(panelAttachable->_facingRotation)
|
||||
{
|
||||
}
|
||||
|
||||
void PanelAttachable::setTransforms(Transform& transform) {
|
||||
if (getAttachedPanel()) {
|
||||
transform.setTranslation(getAttachedPanel()->getAnchorPosition());
|
||||
transform.setRotation(getAttachedPanel()->getOffsetRotation());
|
||||
transform.postTranslate(getOffsetPosition() + getAttachedPanel()->getOffsetPosition());
|
||||
transform.postRotate(getFacingRotation() * getAttachedPanel()->getFacingRotation());
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue PanelAttachable::getProperty(QScriptEngine* scriptEngine, const QString &property) {
|
||||
if (property == "offsetPosition") {
|
||||
return vec3toScriptValue(scriptEngine, getOffsetPosition());
|
||||
}
|
||||
if (property == "facingRotation") {
|
||||
return quatToScriptValue(scriptEngine, getFacingRotation());
|
||||
}
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
void PanelAttachable::setProperties(const QScriptValue &properties) {
|
||||
QScriptValue offsetPosition = properties.property("offsetPosition");
|
||||
if (offsetPosition.isValid()) {
|
||||
QScriptValue x = offsetPosition.property("x");
|
||||
QScriptValue y = offsetPosition.property("y");
|
||||
QScriptValue z = offsetPosition.property("z");
|
||||
|
||||
if (x.isValid() && y.isValid() && z.isValid()) {
|
||||
glm::vec3 newPosition;
|
||||
newPosition.x = x.toVariant().toFloat();
|
||||
newPosition.y = y.toVariant().toFloat();
|
||||
newPosition.z = z.toVariant().toFloat();
|
||||
setOffsetPosition(newPosition);
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue facingRotation = properties.property("facingRotation");
|
||||
if (facingRotation.isValid()) {
|
||||
QScriptValue x = facingRotation.property("x");
|
||||
QScriptValue y = facingRotation.property("y");
|
||||
QScriptValue z = facingRotation.property("z");
|
||||
QScriptValue w = facingRotation.property("w");
|
||||
|
||||
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
|
||||
glm::quat newRotation;
|
||||
newRotation.x = x.toVariant().toFloat();
|
||||
newRotation.y = y.toVariant().toFloat();
|
||||
newRotation.z = z.toVariant().toFloat();
|
||||
newRotation.w = w.toVariant().toFloat();
|
||||
setFacingRotation(newRotation);
|
||||
}
|
||||
}
|
||||
}
|
45
interface/src/ui/overlays/PanelAttachable.h
Normal file
45
interface/src/ui/overlays/PanelAttachable.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// PanelAttachable.h
|
||||
// interface/src/ui/overlays
|
||||
//
|
||||
// Created by Zander Otavka on 7/1/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_PanelAttachable_h
|
||||
#define hifi_PanelAttachable_h
|
||||
|
||||
#include "FloatingUIPanel.h"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <Transform.h>
|
||||
|
||||
class PanelAttachable {
|
||||
public:
|
||||
PanelAttachable();
|
||||
PanelAttachable(const PanelAttachable* panelAttachable);
|
||||
|
||||
FloatingUIPanel::Pointer getAttachedPanel() const { return _attachedPanel; }
|
||||
glm::vec3 getOffsetPosition() const { return _offsetPosition; }
|
||||
glm::quat getFacingRotation() const { return _facingRotation; }
|
||||
|
||||
void setAttachedPanel(FloatingUIPanel::Pointer panel) { _attachedPanel = panel; }
|
||||
void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; }
|
||||
void setFacingRotation(const glm::quat& rotation) { _facingRotation = rotation; }
|
||||
|
||||
QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property);
|
||||
void setProperties(const QScriptValue& properties);
|
||||
|
||||
protected:
|
||||
virtual void setTransforms(Transform& transform);
|
||||
|
||||
private:
|
||||
FloatingUIPanel::Pointer _attachedPanel;
|
||||
glm::vec3 _offsetPosition;
|
||||
glm::quat _facingRotation;
|
||||
};
|
||||
|
||||
#endif // hifi_PanelAttachable_h
|
|
@ -14,8 +14,15 @@
|
|||
#include <GeometryUtil.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
Planar3DOverlay::Planar3DOverlay() :
|
||||
Base3DOverlay(),
|
||||
_dimensions{1.0f, 1.0f}
|
||||
{
|
||||
}
|
||||
|
||||
Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) :
|
||||
Base3DOverlay(planar3DOverlay)
|
||||
Base3DOverlay(planar3DOverlay),
|
||||
_dimensions(planar3DOverlay->_dimensions)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class Planar3DOverlay : public Base3DOverlay {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Planar3DOverlay() {}
|
||||
Planar3DOverlay();
|
||||
Planar3DOverlay(const Planar3DOverlay* planar3DOverlay);
|
||||
|
||||
AABox getBounds() const;
|
||||
|
@ -32,7 +32,7 @@ public:
|
|||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face);
|
||||
|
||||
protected:
|
||||
glm::vec2 _dimensions{1.0f, 1.0f};
|
||||
glm::vec2 _dimensions;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
#include <GeometryCache.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
|
||||
QString const Rectangle3DOverlay::TYPE = "rectangle3d";
|
||||
|
||||
Rectangle3DOverlay::Rectangle3DOverlay() :
|
||||
_geometryCacheID(DependencyManager::get<GeometryCache>()->allocateID())
|
||||
{
|
||||
|
|
|
@ -17,6 +17,9 @@ class Rectangle3DOverlay : public Planar3DOverlay {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
Rectangle3DOverlay();
|
||||
Rectangle3DOverlay(const Rectangle3DOverlay* rectangle3DOverlay);
|
||||
~Rectangle3DOverlay();
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <gpu/Batch.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
QString const Sphere3DOverlay::TYPE = "sphere";
|
||||
|
||||
Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
|
||||
Volume3DOverlay(Sphere3DOverlay)
|
||||
{
|
||||
|
|
|
@ -17,6 +17,9 @@ class Sphere3DOverlay : public Volume3DOverlay {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
Sphere3DOverlay() {}
|
||||
Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay);
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ const int FIXED_FONT_POINT_SIZE = 40;
|
|||
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 80.0f; // this is a ratio determined through experimentation
|
||||
const float LINE_SCALE_RATIO = 1.2f;
|
||||
|
||||
QString const Text3DOverlay::TYPE = "text3d";
|
||||
|
||||
Text3DOverlay::Text3DOverlay() :
|
||||
_backgroundColor(DEFAULT_BACKGROUND_COLOR),
|
||||
_backgroundAlpha(DEFAULT_BACKGROUND_ALPHA),
|
||||
|
|
|
@ -21,6 +21,9 @@ class Text3DOverlay : public Planar3DOverlay {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
Text3DOverlay();
|
||||
Text3DOverlay(const Text3DOverlay* text3DOverlay);
|
||||
~Text3DOverlay();
|
||||
|
|
|
@ -78,6 +78,8 @@ QString toQmlColor(const glm::vec4& v) {
|
|||
arg((int)(v.b * 255), 2, 16, QChar('0'));
|
||||
}
|
||||
|
||||
QString const TextOverlay::TYPE = "text";
|
||||
|
||||
TextOverlay::TextOverlay() :
|
||||
_backgroundColor(DEFAULT_BACKGROUND_COLOR),
|
||||
_backgroundAlpha(DEFAULT_BACKGROUND_ALPHA),
|
||||
|
|
|
@ -29,6 +29,9 @@ class TextOverlay : public Overlay2D {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QString const TYPE;
|
||||
virtual QString getType() const { return TYPE; }
|
||||
|
||||
TextOverlay();
|
||||
TextOverlay(const TextOverlay* textOverlay);
|
||||
~TextOverlay();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AnimationCache.cpp
|
||||
// libraries/script-engine/src/
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Andrzej Kapolka on 4/14/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AnimationCache.h
|
||||
// libraries/script-engine/src/
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Andrzej Kapolka on 4/14/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AnimationHandle.cpp
|
||||
// interface/src/renderer
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/18/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -10,7 +10,7 @@
|
|||
//
|
||||
|
||||
#include "AnimationHandle.h"
|
||||
#include "Model.h"
|
||||
|
||||
|
||||
void AnimationHandle::setURL(const QUrl& url) {
|
||||
if (_url != url) {
|
||||
|
@ -20,28 +20,17 @@ void AnimationHandle::setURL(const QUrl& url) {
|
|||
}
|
||||
}
|
||||
|
||||
static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
|
||||
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
|
||||
if (handle->getPriority() > (*it)->getPriority()) {
|
||||
handles.insert(it, handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
handles.append(handle);
|
||||
}
|
||||
|
||||
void AnimationHandle::setPriority(float priority) {
|
||||
if (_priority == priority) {
|
||||
return;
|
||||
}
|
||||
if (isRunning()) {
|
||||
_model->_runningAnimations.removeOne(_self);
|
||||
_rig->removeRunningAnimation(getAnimationHandlePointer());
|
||||
if (priority < _priority) {
|
||||
replaceMatchingPriorities(priority);
|
||||
}
|
||||
_priority = priority;
|
||||
insertSorted(_model->_runningAnimations, _self);
|
||||
|
||||
_rig->addRunningAnimation(getAnimationHandlePointer());
|
||||
} else {
|
||||
_priority = priority;
|
||||
}
|
||||
|
@ -68,21 +57,21 @@ void AnimationHandle::setRunning(bool running) {
|
|||
}
|
||||
_animationLoop.setRunning(running);
|
||||
if (isRunning()) {
|
||||
if (!_model->_runningAnimations.contains(_self)) {
|
||||
insertSorted(_model->_runningAnimations, _self);
|
||||
if (!_rig->isRunningAnimation(getAnimationHandlePointer())) {
|
||||
_rig->addRunningAnimation(getAnimationHandlePointer());
|
||||
}
|
||||
} else {
|
||||
_model->_runningAnimations.removeOne(_self);
|
||||
_rig->removeRunningAnimation(getAnimationHandlePointer());
|
||||
restoreJoints();
|
||||
replaceMatchingPriorities(0.0f);
|
||||
}
|
||||
emit runningChanged(isRunning());
|
||||
}
|
||||
|
||||
AnimationHandle::AnimationHandle(Model* model) :
|
||||
QObject(model),
|
||||
_model(model),
|
||||
_priority(1.0f)
|
||||
AnimationHandle::AnimationHandle(RigPointer rig) :
|
||||
QObject(rig.get()),
|
||||
_rig(rig),
|
||||
_priority(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -110,42 +99,61 @@ void AnimationHandle::setAnimationDetails(const AnimationDetails& details) {
|
|||
}
|
||||
|
||||
|
||||
void AnimationHandle::setJointMappings(QVector<int> jointMappings) {
|
||||
_jointMappings = jointMappings;
|
||||
}
|
||||
|
||||
QVector<int> AnimationHandle::getJointMappings() {
|
||||
if (_jointMappings.isEmpty()) {
|
||||
QVector<FBXJoint> animationJoints = _animation->getGeometry().joints;
|
||||
for (int i = 0; i < animationJoints.count(); i++) {
|
||||
_jointMappings.append(_rig->indexOfJoint(animationJoints.at(i).name));
|
||||
}
|
||||
}
|
||||
return _jointMappings;
|
||||
}
|
||||
|
||||
void AnimationHandle::simulate(float deltaTime) {
|
||||
if (!_animation || !_animation->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_animationLoop.simulate(deltaTime);
|
||||
|
||||
// update the joint mappings if necessary/possible
|
||||
if (_jointMappings.isEmpty()) {
|
||||
if (_model && _model->isActive()) {
|
||||
_jointMappings = _model->getGeometry()->getJointMappings(_animation);
|
||||
}
|
||||
if (_jointMappings.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!_maskedJoints.isEmpty()) {
|
||||
const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry();
|
||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
int& mapping = _jointMappings[i];
|
||||
if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) {
|
||||
mapping = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getJointMappings().isEmpty()) {
|
||||
qDebug() << "AnimationHandle::simulate -- _jointMappings.isEmpty()";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// // update the joint mappings if necessary/possible
|
||||
// if (_jointMappings.isEmpty()) {
|
||||
// if (_model && _model->isActive()) {
|
||||
// _jointMappings = _model->getGeometry()->getJointMappings(_animation);
|
||||
// }
|
||||
// if (_jointMappings.isEmpty()) {
|
||||
// return;
|
||||
// }
|
||||
// if (!_maskedJoints.isEmpty()) {
|
||||
// const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry();
|
||||
// for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
// int& mapping = _jointMappings[i];
|
||||
// if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) {
|
||||
// mapping = -1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
const FBXGeometry& animationGeometry = _animation->getGeometry();
|
||||
if (animationGeometry.animationFrames.isEmpty()) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (_animationLoop.getMaxFrameIndexHint() != animationGeometry.animationFrames.size()) {
|
||||
_animationLoop.setMaxFrameIndexHint(animationGeometry.animationFrames.size());
|
||||
}
|
||||
|
||||
|
||||
// blend between the closest two frames
|
||||
applyFrame(getFrameIndex());
|
||||
}
|
||||
|
@ -154,17 +162,23 @@ void AnimationHandle::applyFrame(float frameIndex) {
|
|||
if (!_animation || !_animation->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const FBXGeometry& animationGeometry = _animation->getGeometry();
|
||||
int frameCount = animationGeometry.animationFrames.size();
|
||||
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount);
|
||||
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount);
|
||||
float frameFraction = glm::fract(frameIndex);
|
||||
assert(_rig->getJointStateCount() >= _jointMappings.size());
|
||||
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
JointState& state = _model->_jointStates[mapping];
|
||||
state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction), _priority);
|
||||
_rig->setJointRotationInConstrainedFrame(mapping,
|
||||
safeMix(floorFrame.rotations.at(i),
|
||||
ceilFrame.rotations.at(i),
|
||||
frameFraction),
|
||||
_priority,
|
||||
false,
|
||||
_mix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,9 +187,8 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) {
|
|||
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
JointState& state = _model->_jointStates[mapping];
|
||||
if (_priority == state._animationPriority) {
|
||||
state._animationPriority = newPriority;
|
||||
if (_priority == _rig->getJointAnimatinoPriority(mapping)) {
|
||||
_rig->setJointAnimatinoPriority(mapping, newPriority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,9 +198,7 @@ void AnimationHandle::restoreJoints() {
|
|||
for (int i = 0; i < _jointMappings.size(); i++) {
|
||||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
JointState& state = _model->_jointStates[mapping];
|
||||
state.restoreRotation(1.0f, state._animationPriority);
|
||||
_rig->restoreJointRotation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AnimationHandle.h
|
||||
// interface/src/renderer
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/18/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -18,22 +18,43 @@
|
|||
#include <QUrl>
|
||||
#include <QVector>
|
||||
|
||||
#include <AnimationCache.h>
|
||||
#include <AnimationLoop.h>
|
||||
#include "AnimationCache.h"
|
||||
#include "AnimationLoop.h"
|
||||
#include "Rig.h"
|
||||
|
||||
class AnimationHandle;
|
||||
class Model;
|
||||
|
||||
typedef QSharedPointer<AnimationHandle> AnimationHandlePointer;
|
||||
typedef QWeakPointer<AnimationHandle> WeakAnimationHandlePointer;
|
||||
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
|
||||
typedef std::weak_ptr<AnimationHandle> WeakAnimationHandlePointer;
|
||||
inline uint qHash(const std::shared_ptr<AnimationHandle>& a, uint seed) {
|
||||
// return qHash(a.get(), seed);
|
||||
AnimationHandle* strongRef = a ? a.get() : nullptr;
|
||||
return qHash(strongRef, seed);
|
||||
}
|
||||
inline uint qHash(const std::weak_ptr<AnimationHandle>& a, uint seed) {
|
||||
AnimationHandlePointer strongPointer = a.lock();
|
||||
AnimationHandle* strongRef = strongPointer ? strongPointer.get() : nullptr;
|
||||
return qHash(strongRef, seed);
|
||||
}
|
||||
|
||||
|
||||
/// Represents a handle to a model animation.
|
||||
class AnimationHandle : public QObject {
|
||||
// inline uint qHash(const WeakAnimationHandlePointer& handle, uint seed) {
|
||||
// return qHash(handle.data(), seed);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/// Represents a handle to a model animation. I.e., an Animation in use by a given Rig.
|
||||
class AnimationHandle : public QObject, public std::enable_shared_from_this<AnimationHandle> {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
|
||||
AnimationHandle(RigPointer rig);
|
||||
|
||||
AnimationHandlePointer getAnimationHandlePointer() { return shared_from_this(); }
|
||||
|
||||
void setRole(const QString& role) { _role = role; }
|
||||
const QString& getRole() const { return _role; }
|
||||
|
||||
|
@ -42,29 +63,30 @@ public:
|
|||
|
||||
void setPriority(float priority);
|
||||
float getPriority() const { return _priority; }
|
||||
void setMix(float mix) { _mix = mix; }
|
||||
|
||||
void setMaskedJoints(const QStringList& maskedJoints);
|
||||
const QStringList& getMaskedJoints() const { return _maskedJoints; }
|
||||
|
||||
|
||||
|
||||
void setFPS(float fps) { _animationLoop.setFPS(fps); }
|
||||
float getFPS() const { return _animationLoop.getFPS(); }
|
||||
|
||||
void setLoop(bool loop) { _animationLoop.setLoop(loop); }
|
||||
bool getLoop() const { return _animationLoop.getLoop(); }
|
||||
|
||||
|
||||
void setHold(bool hold) { _animationLoop.setHold(hold); }
|
||||
bool getHold() const { return _animationLoop.getHold(); }
|
||||
|
||||
|
||||
void setStartAutomatically(bool startAutomatically);
|
||||
bool getStartAutomatically() const { return _animationLoop.getStartAutomatically(); }
|
||||
|
||||
|
||||
void setFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); }
|
||||
float getFirstFrame() const { return _animationLoop.getFirstFrame(); }
|
||||
|
||||
|
||||
void setLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
|
||||
float getLastFrame() const { return _animationLoop.getLastFrame(); }
|
||||
|
||||
|
||||
void setRunning(bool running);
|
||||
bool isRunning() const { return _animationLoop.isRunning(); }
|
||||
|
||||
|
@ -74,39 +96,38 @@ public:
|
|||
AnimationDetails getAnimationDetails() const;
|
||||
void setAnimationDetails(const AnimationDetails& details);
|
||||
|
||||
void setJointMappings(QVector<int> jointMappings);
|
||||
QVector<int> getJointMappings(); // computing if necessary
|
||||
void simulate(float deltaTime);
|
||||
void applyFrame(float frameIndex);
|
||||
void replaceMatchingPriorities(float newPriority);
|
||||
void restoreJoints();
|
||||
void clearJoints() { _jointMappings.clear(); }
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
void runningChanged(bool running);
|
||||
|
||||
public slots:
|
||||
|
||||
void start() { setRunning(true); }
|
||||
void stop() { setRunning(false); }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
friend class Model;
|
||||
|
||||
AnimationHandle(Model* model);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
void applyFrame(float frameIndex);
|
||||
void replaceMatchingPriorities(float newPriority);
|
||||
void restoreJoints();
|
||||
|
||||
void clearJoints() { _jointMappings.clear(); }
|
||||
|
||||
Model* _model;
|
||||
WeakAnimationHandlePointer _self;
|
||||
RigPointer _rig;
|
||||
AnimationPointer _animation;
|
||||
QString _role;
|
||||
QUrl _url;
|
||||
float _priority;
|
||||
float _mix;
|
||||
|
||||
QStringList _maskedJoints;
|
||||
QVector<int> _jointMappings;
|
||||
|
||||
|
||||
AnimationLoop _animationLoop;
|
||||
|
||||
static QHash<QWeakPointer<Animation>, QVector<int>> _jointMappingsCache;
|
||||
};
|
||||
|
||||
|
14
libraries/animation/src/AnimationLogging.cpp
Normal file
14
libraries/animation/src/AnimationLogging.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// AnimationLogging.cpp
|
||||
// libraries/audio/src
|
||||
//
|
||||
// Created by Howard Stearns on 7/24/15.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimationLogging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(animation, "hifi.animation")
|
18
libraries/animation/src/AnimationLogging.h
Normal file
18
libraries/animation/src/AnimationLogging.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// AnimationLogging.h
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Howard Stearns on 7/24/15.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AnimationLogging_h
|
||||
#define hifi_AnimationLogging_h
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(animation)
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AnimationLoop.cpp
|
||||
// libraries/animation
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 11/12/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AnimationLoop.h
|
||||
// libraries/script-engine/src/
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 11/12/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AnimationObject.cpp
|
||||
// libraries/script-engine/src/
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Andrzej Kapolka on 4/17/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// AnimationObject.h
|
||||
// libraries/script-engine/src/
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Andrzej Kapolka on 4/17/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
|
|
34
libraries/animation/src/AvatarRig.cpp
Normal file
34
libraries/animation/src/AvatarRig.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// AvatarRig.cpp
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by SethAlves on 2015-7-22.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AvatarRig.h"
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) {
|
||||
if (index < 0 && index >= _jointStates.size()) {
|
||||
return; // bail
|
||||
}
|
||||
JointState& state = _jointStates[index];
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
|
||||
// compute model transforms
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
state.computeTransform(parentTransform);
|
||||
clearJointTransformTranslation(index);
|
||||
} else {
|
||||
// guard against out-of-bounds access to _jointStates
|
||||
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
|
||||
const JointState& parentState = _jointStates.at(parentIndex);
|
||||
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
|
||||
}
|
||||
}
|
||||
}
|
27
libraries/animation/src/AvatarRig.h
Normal file
27
libraries/animation/src/AvatarRig.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// AvatarRig.h
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by SethAlves on 2015-7-22.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AvatarRig_h
|
||||
#define hifi_AvatarRig_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "Rig.h"
|
||||
|
||||
class AvatarRig : public Rig {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~AvatarRig() {}
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarRig_h
|
30
libraries/animation/src/EntityRig.cpp
Normal file
30
libraries/animation/src/EntityRig.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// EntityRig.cpp
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by SethAlves on 2015-7-22.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntityRig.h"
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
void EntityRig::updateJointState(int index, glm::mat4 parentTransform) {
|
||||
JointState& state = _jointStates[index];
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
|
||||
// compute model transforms
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
state.computeTransform(parentTransform);
|
||||
} else {
|
||||
// guard against out-of-bounds access to _jointStates
|
||||
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
|
||||
const JointState& parentState = _jointStates.at(parentIndex);
|
||||
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
|
||||
}
|
||||
}
|
||||
}
|
27
libraries/animation/src/EntityRig.h
Normal file
27
libraries/animation/src/EntityRig.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// EntityRig.h
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by SethAlves on 2015-7-22.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_EntityRig_h
|
||||
#define hifi_EntityRig_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "Rig.h"
|
||||
|
||||
class EntityRig : public Rig {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~EntityRig() {}
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||
};
|
||||
|
||||
#endif // hifi_EntityRig_h
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// JointState.cpp
|
||||
// interface/src/renderer
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/18/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -232,12 +232,13 @@ glm::quat JointState::computeVisibleParentRotation() const {
|
|||
return _visibleRotation * glm::inverse(_fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation);
|
||||
}
|
||||
|
||||
void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain) {
|
||||
void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain, float mix) {
|
||||
if (priority >= _animationPriority || _animationPriority == 0.0f) {
|
||||
if (constrain && _constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
|
||||
}
|
||||
setRotationInConstrainedFrameInternal(targetRotation);
|
||||
auto rotation = (mix == 1.0f) ? targetRotation : safeMix(getRotationInConstrainedFrame(), targetRotation, mix);
|
||||
setRotationInConstrainedFrameInternal(rotation);
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// JointState.h
|
||||
// interface/src/renderer
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/18/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
|
@ -84,7 +84,7 @@ public:
|
|||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
|
||||
|
||||
void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false);
|
||||
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; }
|
||||
const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; }
|
754
libraries/animation/src/Rig.cpp
Normal file
754
libraries/animation/src/Rig.cpp
Normal file
|
@ -0,0 +1,754 @@
|
|||
//
|
||||
// Rig.cpp
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15.
|
||||
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <queue>
|
||||
|
||||
#include "AnimationHandle.h"
|
||||
#include "AnimationLogging.h"
|
||||
#include "Rig.h"
|
||||
|
||||
void Rig::HeadParameters::dump() const {
|
||||
qCDebug(animation, "HeadParameters =");
|
||||
qCDebug(animation, " leanSideways = %0.5f", leanSideways);
|
||||
qCDebug(animation, " leanForward = %0.5f", leanForward);
|
||||
qCDebug(animation, " torsoTwist = %0.5f", torsoTwist);
|
||||
glm::vec3 axis = glm::axis(localHeadOrientation);
|
||||
float theta = glm::angle(localHeadOrientation);
|
||||
qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
|
||||
axis = glm::axis(worldHeadOrientation);
|
||||
theta = glm::angle(worldHeadOrientation);
|
||||
qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", axis.x, axis.y, axis.z, theta);
|
||||
qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", eyeLookAt.x, eyeLookAt.y, eyeLookAt.z);
|
||||
qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", eyeSaccade.x, eyeSaccade.y, eyeSaccade.z);
|
||||
qCDebug(animation, " leanJointIndex = %.d", leanJointIndex);
|
||||
qCDebug(animation, " neckJointIndex = %.d", neckJointIndex);
|
||||
qCDebug(animation, " leftEyeJointIndex = %.d", leftEyeJointIndex);
|
||||
qCDebug(animation, " rightEyeJointIndex = %.d", rightEyeJointIndex);
|
||||
}
|
||||
|
||||
void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
|
||||
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
|
||||
if (handle->getPriority() > (*it)->getPriority()) {
|
||||
handles.insert(it, handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
handles.append(handle);
|
||||
}
|
||||
|
||||
AnimationHandlePointer Rig::createAnimationHandle() {
|
||||
AnimationHandlePointer handle(new AnimationHandle(getRigPointer()));
|
||||
_animationHandles.append(handle);
|
||||
return handle;
|
||||
}
|
||||
void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) {
|
||||
handle->stop();
|
||||
// FIXME? Do we need to also animationHandle->clearJoints()? deleteAnimations(), below, was first written to do so, but did not first stop it.
|
||||
_animationHandles.removeOne(handle);
|
||||
}
|
||||
|
||||
void Rig::startAnimation(const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
|
||||
//qCDebug(animation) << "startAnimation" << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints;
|
||||
foreach (const AnimationHandlePointer& candidate, _animationHandles) {
|
||||
if (candidate->getURL() == url) {
|
||||
candidate->start();
|
||||
return;
|
||||
}
|
||||
}
|
||||
AnimationHandlePointer handle = createAnimationHandle();
|
||||
handle->setURL(url);
|
||||
handle->setFPS(fps);
|
||||
handle->setPriority(priority);
|
||||
handle->setLoop(loop);
|
||||
handle->setHold(hold);
|
||||
handle->setFirstFrame(firstFrame);
|
||||
handle->setLastFrame(lastFrame);
|
||||
handle->setMaskedJoints(maskedJoints);
|
||||
handle->start();
|
||||
}
|
||||
|
||||
void Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically) {
|
||||
// check for a configured animation for the role
|
||||
//qCDebug(animation) << "addAnimationByRole" << role << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints << startAutomatically;
|
||||
foreach (const AnimationHandlePointer& candidate, _animationHandles) {
|
||||
if (candidate->getRole() == role) {
|
||||
if (startAutomatically) {
|
||||
candidate->start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
AnimationHandlePointer handle = createAnimationHandle();
|
||||
handle->setRole(role);
|
||||
handle->setURL(url);
|
||||
handle->setFPS(fps);
|
||||
handle->setPriority(priority);
|
||||
handle->setLoop(loop);
|
||||
handle->setHold(hold);
|
||||
handle->setFirstFrame(firstFrame);
|
||||
handle->setLastFrame(lastFrame);
|
||||
handle->setMaskedJoints(maskedJoints);
|
||||
if (startAutomatically) {
|
||||
handle->start();
|
||||
}
|
||||
}
|
||||
void Rig::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
|
||||
addAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints, true);
|
||||
}
|
||||
|
||||
void Rig::stopAnimationByRole(const QString& role) {
|
||||
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
|
||||
if (handle->getRole() == role) {
|
||||
handle->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::stopAnimation(const QString& url) {
|
||||
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
|
||||
if (handle->getURL() == url) {
|
||||
handle->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) {
|
||||
return _runningAnimations.removeOne(animationHandle);
|
||||
}
|
||||
|
||||
void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) {
|
||||
insertSorted(_runningAnimations, animationHandle);
|
||||
}
|
||||
|
||||
bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
|
||||
return _runningAnimations.contains(animationHandle);
|
||||
}
|
||||
|
||||
void Rig::deleteAnimations() {
|
||||
for (auto animation : _animationHandles) {
|
||||
removeAnimationHandle(animation);
|
||||
}
|
||||
_animationHandles.clear();
|
||||
}
|
||||
|
||||
float Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform) {
|
||||
_jointStates = states;
|
||||
initJointTransforms(parentTransform);
|
||||
|
||||
int numStates = _jointStates.size();
|
||||
float radius = 0.0f;
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
float distance = glm::length(_jointStates[i].getPosition());
|
||||
if (distance > radius) {
|
||||
radius = distance;
|
||||
}
|
||||
_jointStates[i].buildConstraint();
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].slaveVisibleTransform();
|
||||
}
|
||||
|
||||
return radius;
|
||||
}
|
||||
|
||||
// We could build and cache a dictionary, too....
|
||||
// Should we be using .fst mapping instead/also?
|
||||
int Rig::indexOfJoint(const QString& jointName) {
|
||||
for (int i = 0; i < _jointStates.count(); i++) {
|
||||
if (_jointStates[i].getFBXJoint().name == jointName) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void Rig::initJointTransforms(glm::mat4 parentTransform) {
|
||||
// compute model transforms
|
||||
int numStates = _jointStates.size();
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
JointState& state = _jointStates[i];
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
state.initTransform(parentTransform);
|
||||
} else {
|
||||
const JointState& parentState = _jointStates.at(parentIndex);
|
||||
state.initTransform(parentState.getTransform());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::clearJointTransformTranslation(int jointIndex) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return;
|
||||
}
|
||||
_jointStates[jointIndex].clearTransformTranslation();
|
||||
}
|
||||
|
||||
void Rig::reset(const QVector<FBXJoint>& fbxJoints) {
|
||||
if (_jointStates.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
JointState Rig::getJointState(int jointIndex) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return JointState();
|
||||
}
|
||||
return _jointStates[jointIndex];
|
||||
}
|
||||
|
||||
bool Rig::getJointStateRotation(int index, glm::quat& rotation) const {
|
||||
if (index == -1 || index >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
const JointState& state = _jointStates.at(index);
|
||||
rotation = state.getRotationInConstrainedFrame();
|
||||
return !state.rotationIsDefault(rotation);
|
||||
}
|
||||
|
||||
bool Rig::getVisibleJointState(int index, glm::quat& rotation) const {
|
||||
if (index == -1 || index >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
const JointState& state = _jointStates.at(index);
|
||||
rotation = state.getVisibleRotationInConstrainedFrame();
|
||||
return !state.rotationIsDefault(rotation);
|
||||
}
|
||||
|
||||
void Rig::clearJointState(int index) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
state.setRotationInConstrainedFrame(glm::quat(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::clearJointStates() {
|
||||
_jointStates.clear();
|
||||
}
|
||||
|
||||
void Rig::clearJointAnimationPriority(int index) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
_jointStates[index]._animationPriority = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float Rig::getJointAnimatinoPriority(int index) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
return _jointStates[index]._animationPriority;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void Rig::setJointAnimatinoPriority(int index, float newPriority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
_jointStates[index]._animationPriority = newPriority;
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
if (valid) {
|
||||
state.setRotationInConstrainedFrame(rotation, priority);
|
||||
} else {
|
||||
state.restoreRotation(1.0f, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::restoreJointRotation(int index, float fraction, float priority) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
_jointStates[index].restoreRotation(fraction, priority);
|
||||
}
|
||||
}
|
||||
|
||||
bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
|
||||
glm::vec3 translation, glm::quat rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
// position is in world-frame
|
||||
position = translation + rotation * _jointStates[jointIndex].getPosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
// position is in model-frame
|
||||
position = extractTranslation(_jointStates[jointIndex].getTransform());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
result = rotation * _jointStates[jointIndex].getRotation();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
rotation = _jointStates[jointIndex].getRotation();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
result = rotation * _jointStates[jointIndex].getRotation();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Rig::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
|
||||
glm::vec3 translation, glm::quat rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
// position is in world-frame
|
||||
position = translation + rotation * _jointStates[jointIndex].getVisiblePosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rig::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
result = rotation * _jointStates[jointIndex].getVisibleRotation();
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::mat4 Rig::getJointTransform(int jointIndex) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return glm::mat4();
|
||||
}
|
||||
return _jointStates[jointIndex].getTransform();
|
||||
}
|
||||
|
||||
glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return glm::mat4();
|
||||
}
|
||||
return _jointStates[jointIndex].getVisibleTransform();
|
||||
}
|
||||
|
||||
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
|
||||
if (_enableRig) {
|
||||
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
||||
float forwardSpeed = glm::dot(worldVelocity, front);
|
||||
float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime;
|
||||
bool isWalking = std::abs(forwardSpeed) > 0.01f;
|
||||
bool isTurning = std::abs(rotationalSpeed) > 0.5f;
|
||||
|
||||
// Crude, until we have blending:
|
||||
isTurning = isTurning && !isWalking; // Only one of walk/turn, walk wins.
|
||||
isTurning = false; // FIXME
|
||||
bool isIdle = !isWalking && !isTurning;
|
||||
auto singleRole = [](bool walking, bool turning, bool idling) {
|
||||
return walking ? "walk" : (turning ? "turn" : (idling ? "idle" : ""));
|
||||
};
|
||||
QString toStop = singleRole(_isWalking && !isWalking, _isTurning && !isTurning, _isIdle && !isIdle);
|
||||
if (!toStop.isEmpty()) {
|
||||
//qCDebug(animation) << "isTurning" << isTurning << "fronts" << front << _lastFront << glm::angle(front, _lastFront) << rotationalSpeed;
|
||||
stopAnimationByRole(toStop);
|
||||
}
|
||||
QString newRole = singleRole(isWalking && !_isWalking, isTurning && !_isTurning, isIdle && !_isIdle);
|
||||
if (!newRole.isEmpty()) {
|
||||
startAnimationByRole(newRole);
|
||||
qCDebug(animation) << deltaTime << ":" << worldVelocity << "." << front << "=> " << forwardSpeed << newRole;
|
||||
}
|
||||
|
||||
_lastPosition = worldPosition;
|
||||
_lastFront = front;
|
||||
_isWalking = isWalking;
|
||||
_isTurning = isTurning;
|
||||
_isIdle = isIdle;
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
||||
int nAnimationsSoFar = 0;
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
handle->setMix(1.0f / ++nAnimationsSoFar);
|
||||
handle->setPriority(1.0);
|
||||
handle->simulate(deltaTime);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i, parentTransform);
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].resetTransformChanged();
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (freeLineage.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (lastFreeIndex == -1) {
|
||||
lastFreeIndex = freeLineage.last();
|
||||
}
|
||||
|
||||
// this is a cyclic coordinate descent algorithm: see
|
||||
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
|
||||
const int ITERATION_COUNT = 1;
|
||||
glm::vec3 worldAlignment = alignment;
|
||||
for (int i = 0; i < ITERATION_COUNT; i++) {
|
||||
// first, try to rotate the end effector as close as possible to the target rotation, if any
|
||||
glm::quat endRotation;
|
||||
if (useRotation) {
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
|
||||
state.setRotationInBindFrame(rotation, priority);
|
||||
endRotation = state.getRotationInBindFrame();
|
||||
}
|
||||
|
||||
// then, we go from the joint upwards, rotating the end as close as possible to the target
|
||||
glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform());
|
||||
for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) {
|
||||
int index = freeLineage.at(j);
|
||||
JointState& state = _jointStates[index];
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
if (!(joint.isFree || allIntermediatesFree)) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 jointPosition = extractTranslation(state.getTransform());
|
||||
glm::vec3 jointVector = endPosition - jointPosition;
|
||||
glm::quat oldCombinedRotation = state.getRotation();
|
||||
glm::quat combinedDelta;
|
||||
float combinedWeight;
|
||||
if (useRotation) {
|
||||
combinedDelta = safeMix(rotation * glm::inverse(endRotation),
|
||||
rotationBetween(jointVector, position - jointPosition), 0.5f);
|
||||
combinedWeight = 2.0f;
|
||||
|
||||
} else {
|
||||
combinedDelta = rotationBetween(jointVector, position - jointPosition);
|
||||
combinedWeight = 1.0f;
|
||||
}
|
||||
if (alignment != glm::vec3() && j > 1) {
|
||||
jointVector = endPosition - jointPosition;
|
||||
glm::vec3 positionSum;
|
||||
for (int k = j - 1; k > 0; k--) {
|
||||
int index = freeLineage.at(k);
|
||||
updateJointState(index, parentTransform);
|
||||
positionSum += extractTranslation(_jointStates.at(index).getTransform());
|
||||
}
|
||||
glm::vec3 projectedCenterOfMass = glm::cross(jointVector,
|
||||
glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector));
|
||||
glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector));
|
||||
const float LENGTH_EPSILON = 0.001f;
|
||||
if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) {
|
||||
combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment),
|
||||
1.0f / (combinedWeight + 1.0f));
|
||||
}
|
||||
}
|
||||
state.applyRotationDelta(combinedDelta, true, priority);
|
||||
glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation);
|
||||
endPosition = actualDelta * jointVector + jointPosition;
|
||||
if (useRotation) {
|
||||
endRotation = actualDelta * endRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now update the joint states from the top
|
||||
for (int j = freeLineage.size() - 1; j >= 0; j--) {
|
||||
updateJointState(freeLineage.at(j), parentTransform);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if (endIndex == -1 || _jointStates.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (freeLineage.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int numFree = freeLineage.size();
|
||||
|
||||
// store and remember topmost parent transform
|
||||
glm::mat4 topParentTransform;
|
||||
{
|
||||
int index = freeLineage.last();
|
||||
const JointState& state = _jointStates.at(index);
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
topParentTransform = parentTransform;
|
||||
} else {
|
||||
topParentTransform = _jointStates[parentIndex].getTransform();
|
||||
}
|
||||
}
|
||||
|
||||
// this is a cyclic coordinate descent algorithm: see
|
||||
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
|
||||
|
||||
// keep track of the position of the end-effector
|
||||
JointState& endState = _jointStates[endIndex];
|
||||
glm::vec3 endPosition = endState.getPosition();
|
||||
float distanceToGo = glm::distance(targetPosition, endPosition);
|
||||
|
||||
const int MAX_ITERATION_COUNT = 2;
|
||||
const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm
|
||||
int numIterations = 0;
|
||||
do {
|
||||
++numIterations;
|
||||
// moving up, rotate each free joint to get endPosition closer to target
|
||||
for (int j = 1; j < numFree; j++) {
|
||||
int nextIndex = freeLineage.at(j);
|
||||
JointState& nextState = _jointStates[nextIndex];
|
||||
FBXJoint nextJoint = nextState.getFBXJoint();
|
||||
if (! nextJoint.isFree) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glm::vec3 pivot = nextState.getPosition();
|
||||
glm::vec3 leverArm = endPosition - pivot;
|
||||
float leverLength = glm::length(leverArm);
|
||||
if (leverLength < EPSILON) {
|
||||
continue;
|
||||
}
|
||||
glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot);
|
||||
|
||||
// We want to mix the shortest rotation with one that will pull the system down with gravity
|
||||
// so that limbs don't float unrealistically. To do this we compute a simplified center of mass
|
||||
// where each joint has unit mass and we don't bother averaging it because we only need direction.
|
||||
if (j > 1) {
|
||||
|
||||
glm::vec3 centerOfMass(0.0f);
|
||||
for (int k = 0; k < j; ++k) {
|
||||
int massIndex = freeLineage.at(k);
|
||||
centerOfMass += _jointStates[massIndex].getPosition() - pivot;
|
||||
}
|
||||
// the gravitational effect is a rotation that tends to align the two cross products
|
||||
const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f);
|
||||
glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm),
|
||||
glm::cross(worldAlignment, leverArm));
|
||||
|
||||
float gravityAngle = glm::angle(gravityDelta);
|
||||
const float MIN_GRAVITY_ANGLE = 0.1f;
|
||||
float mixFactor = 0.5f;
|
||||
if (gravityAngle < MIN_GRAVITY_ANGLE) {
|
||||
// the final rotation is a mix of the two
|
||||
mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE;
|
||||
}
|
||||
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.
|
||||
glm::quat oldNextRotation = nextState.getRotation();
|
||||
float mixFactor = 0.03f;
|
||||
nextState.mixRotationDelta(deltaRotation, mixFactor, priority);
|
||||
|
||||
// measure the result of the rotation which may have been modified by
|
||||
// blending and constraints
|
||||
glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation);
|
||||
endPosition = pivot + actualDelta * leverArm;
|
||||
}
|
||||
|
||||
// recompute transforms from the top down
|
||||
glm::mat4 currentParentTransform = topParentTransform;
|
||||
for (int j = numFree - 1; j >= 0; --j) {
|
||||
JointState& freeState = _jointStates[freeLineage.at(j)];
|
||||
freeState.computeTransform(currentParentTransform);
|
||||
currentParentTransform = freeState.getTransform();
|
||||
}
|
||||
|
||||
// measure our success
|
||||
endPosition = endState.getPosition();
|
||||
distanceToGo = glm::distance(targetPosition, endPosition);
|
||||
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR);
|
||||
|
||||
// set final rotation of the end joint
|
||||
endState.setRotationInBindFrame(targetRotation, priority, true);
|
||||
}
|
||||
|
||||
bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (int index, freeLineage) {
|
||||
JointState& state = _jointStates[index];
|
||||
state.restoreRotation(fraction, priority);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
float Rig::getLimbLength(int jointIndex, const QVector<int>& freeLineage,
|
||||
const glm::vec3 scale, const QVector<FBXJoint>& fbxJoints) const {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return 0.0f;
|
||||
}
|
||||
float length = 0.0f;
|
||||
float lengthScale = (scale.x + scale.y + scale.z) / 3.0f;
|
||||
for (int i = freeLineage.size() - 2; i >= 0; i--) {
|
||||
length += fbxJoints.at(freeLineage.at(i)).distanceToParent * lengthScale;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) {
|
||||
glm::quat endRotation;
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return endRotation;
|
||||
}
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
state.setRotationInBindFrame(rotation, priority, constrain);
|
||||
endRotation = state.getRotationInBindFrame();
|
||||
return endRotation;
|
||||
}
|
||||
|
||||
glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return glm::vec3();
|
||||
}
|
||||
return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame();
|
||||
}
|
||||
|
||||
glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain, float mix) {
|
||||
glm::quat endRotation;
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return endRotation;
|
||||
}
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
state.setRotationInConstrainedFrame(targetRotation, priority, constrain, mix);
|
||||
endRotation = state.getRotationInConstrainedFrame();
|
||||
return endRotation;
|
||||
}
|
||||
|
||||
void Rig::updateVisibleJointStates() {
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].slaveVisibleTransform();
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::setJointTransform(int jointIndex, glm::mat4 newTransform) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return;
|
||||
}
|
||||
_jointStates[jointIndex].setTransform(newTransform);
|
||||
}
|
||||
|
||||
void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return;
|
||||
}
|
||||
_jointStates[jointIndex].setVisibleTransform(newTransform);
|
||||
}
|
||||
|
||||
void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
_jointStates[jointIndex].applyRotationDelta(delta, constrain, priority);
|
||||
}
|
||||
|
||||
glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return glm::quat();
|
||||
}
|
||||
return _jointStates[jointIndex].getDefaultRotationInParentFrame();
|
||||
}
|
||||
|
||||
void Rig::updateFromHeadParameters(const HeadParameters& params) {
|
||||
updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist);
|
||||
updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist);
|
||||
updateEyeJoint(params.leftEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
|
||||
updateEyeJoint(params.rightEyeJointIndex, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade);
|
||||
}
|
||||
|
||||
void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) {
|
||||
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
|
||||
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
|
||||
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
||||
glm::quat inverse = glm::inverse(parentState.getRotation() * getJointDefaultRotationInParentFrame(index));
|
||||
setJointRotationInConstrainedFrame(index,
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * zAxis) *
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * xAxis) *
|
||||
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * yAxis) *
|
||||
getJointState(index).getFBXJoint().rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist) {
|
||||
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
|
||||
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
|
||||
auto joint = _jointStates[index].getFBXJoint();
|
||||
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
|
||||
glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||
glm::vec3 pitchYawRoll = safeEulerAngles(localHeadOrientation);
|
||||
glm::vec3 lean = glm::radians(glm::vec3(leanForward, torsoTwist, leanSideways));
|
||||
pitchYawRoll -= lean;
|
||||
setJointRotationInConstrainedFrame(index,
|
||||
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) *
|
||||
glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) *
|
||||
glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) *
|
||||
joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade) {
|
||||
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
|
||||
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
|
||||
auto joint = _jointStates[index].getFBXJoint();
|
||||
|
||||
// NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual.
|
||||
glm::mat4 inverse = glm::inverse(parentState.getTransform() *
|
||||
glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
|
||||
glm::vec3 front = glm::vec3(inverse * glm::vec4(worldHeadOrientation * IDENTITY_FRONT, 0.0f));
|
||||
glm::vec3 lookAtDelta = lookAt;
|
||||
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * saccade, 1.0f));
|
||||
glm::quat between = rotationBetween(front, lookAt);
|
||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||
float angle = glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE);
|
||||
glm::quat rot = glm::angleAxis(angle, glm::axis(between));
|
||||
setJointRotationInConstrainedFrame(index, rot * joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
}
|
171
libraries/animation/src/Rig.h
Normal file
171
libraries/animation/src/Rig.h
Normal file
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
// Rig.h
|
||||
// libraries/animation/src/
|
||||
//
|
||||
// Produces animation data and hip placement for the current timestamp.
|
||||
//
|
||||
// Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15.
|
||||
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/*
|
||||
Things we want to be able to do, that I think we cannot do now:
|
||||
* Stop an animation at a given time so that it can be examined visually or in a test harness. (I think we can already stop animation and set frame to a computed float? But does that move the bones?)
|
||||
* Play two animations, blending between them. (Current structure just has one, under script control.)
|
||||
* Fade in an animation over another.
|
||||
* Apply IK, lean, head pointing or other overrides relative to previous position.
|
||||
All of this depends on coordinated state.
|
||||
|
||||
TBD:
|
||||
- What are responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop?
|
||||
Is there common/copied code (e.g., ScriptableAvatar::update)?
|
||||
- How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object
|
||||
physics?
|
||||
- Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the
|
||||
system choosing randomly?
|
||||
|
||||
- Distribute some doc from here to the right files if it turns out to be correct:
|
||||
- AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything
|
||||
equivalent to editEntity.
|
||||
But what's the intended difference vs AnimationObjection? Maybe AnimationDetails is to Animation as AnimationObject
|
||||
is to AnimationPointer?
|
||||
*/
|
||||
|
||||
#ifndef __hifi__Rig__
|
||||
#define __hifi__Rig__
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS
|
||||
|
||||
class AnimationHandle;
|
||||
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
|
||||
// typedef QWeakPointer<AnimationHandle> WeakAnimationHandlePointer;
|
||||
|
||||
class Rig;
|
||||
typedef std::shared_ptr<Rig> RigPointer;
|
||||
|
||||
class Rig : public QObject, public std::enable_shared_from_this<Rig> {
|
||||
|
||||
public:
|
||||
|
||||
struct HeadParameters {
|
||||
float leanSideways = 0.0f; // degrees
|
||||
float leanForward = 0.0f; // degrees
|
||||
float torsoTwist = 0.0f; // degrees
|
||||
glm::quat localHeadOrientation = glm::quat();
|
||||
glm::quat worldHeadOrientation = glm::quat();
|
||||
glm::vec3 eyeLookAt = glm::vec3(); // world space
|
||||
glm::vec3 eyeSaccade = glm::vec3(); // world space
|
||||
int leanJointIndex = -1;
|
||||
int neckJointIndex = -1;
|
||||
int leftEyeJointIndex = -1;
|
||||
int rightEyeJointIndex = -1;
|
||||
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
virtual ~Rig() {}
|
||||
|
||||
RigPointer getRigPointer() { return shared_from_this(); }
|
||||
|
||||
AnimationHandlePointer createAnimationHandle();
|
||||
void removeAnimationHandle(const AnimationHandlePointer& handle);
|
||||
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
|
||||
void addRunningAnimation(AnimationHandlePointer animationHandle);
|
||||
bool isRunningAnimation(AnimationHandlePointer animationHandle);
|
||||
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
|
||||
void deleteAnimations();
|
||||
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
|
||||
void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
|
||||
bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
void stopAnimation(const QString& url);
|
||||
void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
|
||||
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
|
||||
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
void stopAnimationByRole(const QString& role);
|
||||
void addAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
|
||||
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);
|
||||
|
||||
float initJointStates(QVector<JointState> states, glm::mat4 parentTransform);
|
||||
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
|
||||
int getJointStateCount() const { return _jointStates.size(); }
|
||||
int indexOfJoint(const QString& jointName) ;
|
||||
|
||||
void initJointTransforms(glm::mat4 parentTransform);
|
||||
void clearJointTransformTranslation(int jointIndex);
|
||||
void reset(const QVector<FBXJoint>& fbxJoints);
|
||||
bool getJointStateRotation(int index, glm::quat& rotation) const;
|
||||
void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority);
|
||||
JointState getJointState(int jointIndex) const; // XXX
|
||||
bool getVisibleJointState(int index, glm::quat& rotation) const;
|
||||
void clearJointState(int index);
|
||||
void clearJointStates();
|
||||
void clearJointAnimationPriority(int index);
|
||||
float getJointAnimatinoPriority(int index);
|
||||
void setJointAnimatinoPriority(int index, float newPriority);
|
||||
void setJointState(int index, bool valid, const glm::quat& rotation, float priority);
|
||||
void restoreJointRotation(int index, float fraction, float priority);
|
||||
bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
|
||||
glm::vec3 translation, glm::quat rotation) const;
|
||||
|
||||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
|
||||
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
|
||||
bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const;
|
||||
bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
|
||||
glm::vec3 translation, glm::quat rotation) const;
|
||||
bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const;
|
||||
glm::mat4 getJointTransform(int jointIndex) const;
|
||||
void setJointTransform(int jointIndex, glm::mat4 newTransform);
|
||||
glm::mat4 getJointVisibleTransform(int jointIndex) const;
|
||||
void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform);
|
||||
// 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);
|
||||
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);
|
||||
void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform);
|
||||
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;
|
||||
|
||||
glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain = false);
|
||||
glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex);
|
||||
glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation,
|
||||
float priority, bool constrain = false, float mix = 1.0f);
|
||||
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
|
||||
void updateVisibleJointStates();
|
||||
|
||||
virtual void updateJointState(int index, glm::mat4 parentTransform) = 0;
|
||||
|
||||
void setEnableRig(bool isEnabled) { _enableRig = isEnabled; }
|
||||
|
||||
void updateFromHeadParameters(const HeadParameters& params);
|
||||
|
||||
protected:
|
||||
|
||||
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
|
||||
void updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist);
|
||||
void updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||
|
||||
QVector<JointState> _jointStates;
|
||||
|
||||
QList<AnimationHandlePointer> _animationHandles;
|
||||
QList<AnimationHandlePointer> _runningAnimations;
|
||||
|
||||
bool _enableRig;
|
||||
bool _isWalking;
|
||||
bool _isTurning;
|
||||
bool _isIdle;
|
||||
glm::vec3 _lastFront;
|
||||
glm::vec3 _lastPosition;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Rig__) */
|
|
@ -1037,9 +1037,14 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) {
|
|||
localOutput->moveToThread(injector->getLocalBuffer()->thread());
|
||||
|
||||
// have it be stopped when that local buffer is about to close
|
||||
connect(localOutput, &QAudioOutput::stateChanged, this, &AudioClient::audioStateChanged);
|
||||
connect(this, &AudioClient::audioFinished, localOutput, &QAudioOutput::stop);
|
||||
connect(this, &AudioClient::audioFinished, injector, &AudioInjector::stop);
|
||||
// We don't want to stop this localOutput and injector whenever this AudioClient singleton goes idle,
|
||||
// only when the localOutput does. But the connection is to localOutput, so that it happens on the right thread.
|
||||
connect(localOutput, &QAudioOutput::stateChanged, localOutput, [=](QAudio::State state) {
|
||||
if (state == QAudio::IdleState) {
|
||||
localOutput->stop();
|
||||
injector->stop();
|
||||
}
|
||||
});
|
||||
|
||||
connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop);
|
||||
|
||||
|
@ -1358,9 +1363,3 @@ void AudioClient::saveSettings() {
|
|||
windowSecondsForDesiredReduction.set(_receivedAudioStream.getWindowSecondsForDesiredReduction());
|
||||
repetitionWithFade.set(_receivedAudioStream.getRepetitionWithFade());
|
||||
}
|
||||
|
||||
void AudioClient::audioStateChanged(QAudio::State state) {
|
||||
if (state == QAudio::IdleState) {
|
||||
emit audioFinished();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -209,9 +209,6 @@ protected:
|
|||
deleteLater();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void audioStateChanged(QAudio::State state);
|
||||
|
||||
private:
|
||||
void outputFormatChanged();
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "RenderablePolyVoxEntityItem.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
#include "AddressManager.h"
|
||||
#include "EntityRig.h"
|
||||
|
||||
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||
AbstractScriptingServicesInterface* scriptingServices) :
|
||||
|
@ -695,7 +696,7 @@ Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& coll
|
|||
|
||||
return model;
|
||||
}
|
||||
model = new Model();
|
||||
model = new Model(std::make_shared<EntityRig>());
|
||||
model->init();
|
||||
model->setURL(QUrl(url));
|
||||
model->setCollisionModelURL(QUrl(collisionUrl));
|
||||
|
@ -728,7 +729,7 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c
|
|||
}
|
||||
|
||||
// create the model and correctly initialize it with the new url
|
||||
model = new Model();
|
||||
model = new Model(std::make_shared<EntityRig>());
|
||||
model->init();
|
||||
model->setURL(QUrl(newUrl));
|
||||
model->setCollisionModelURL(QUrl(collisionUrl));
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include <PolyVoxCore/Material.h>
|
||||
|
||||
#include "model/Geometry.h"
|
||||
#include "gpu/GLBackend.h"
|
||||
#include "gpu/Context.h"
|
||||
#include "EntityTreeRenderer.h"
|
||||
#include "RenderablePolyVoxEntityItem.h"
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "RenderableWebEntityItem.h"
|
||||
|
||||
#include <gpu/GPUConfig.h>
|
||||
#include <QMouseEvent>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
|
@ -24,7 +23,7 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <PathUtils.h>
|
||||
#include <TextureCache.h>
|
||||
#include <gpu/GLBackend.h>
|
||||
#include <gpu/Context.h>
|
||||
|
||||
#include "EntityTreeRenderer.h"
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
|
|||
}
|
||||
|
||||
Model* RenderableZoneEntityItem::getModel() {
|
||||
Model* model = new Model();
|
||||
Model* model = new Model(nullptr);
|
||||
model->setIsWireframe(true);
|
||||
model->init();
|
||||
return model;
|
||||
|
|
|
@ -114,7 +114,8 @@ _glowLevelChanged(false),
|
|||
_localRenderAlphaChanged(false),
|
||||
|
||||
_defaultSettings(true),
|
||||
_naturalDimensions(1.0f, 1.0f, 1.0f)
|
||||
_naturalDimensions(1.0f, 1.0f, 1.0f),
|
||||
_naturalPosition(0.0f, 0.0f, 0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -128,6 +129,11 @@ void EntityItemProperties::setSittingPoints(const QVector<SittingPoint>& sitting
|
|||
}
|
||||
}
|
||||
|
||||
void EntityItemProperties::calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max) {
|
||||
glm::vec3 halfDimension = (max - min) / 2.0f;
|
||||
_naturalPosition = max - halfDimension;
|
||||
}
|
||||
|
||||
bool EntityItemProperties::animationSettingsChanged() const {
|
||||
return _animationSettingsChanged;
|
||||
}
|
||||
|
@ -378,6 +384,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(dimensions);
|
||||
if (!skipDefaults) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(naturalDimensions); // gettable, but not settable
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(naturalPosition);
|
||||
}
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(rotation);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(velocity);
|
||||
|
|
|
@ -192,7 +192,10 @@ public:
|
|||
|
||||
const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; }
|
||||
void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; }
|
||||
|
||||
|
||||
const glm::vec3& getNaturalPosition() const { return _naturalPosition; }
|
||||
void calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max);
|
||||
|
||||
const QStringList& getTextureNames() const { return _textureNames; }
|
||||
void setTextureNames(const QStringList& value) { _textureNames = value; }
|
||||
|
||||
|
@ -232,6 +235,7 @@ private:
|
|||
QVector<SittingPoint> _sittingPoints;
|
||||
QStringList _textureNames;
|
||||
glm::vec3 _naturalDimensions;
|
||||
glm::vec3 _naturalPosition;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(EntityItemProperties);
|
||||
|
|
|
@ -118,6 +118,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
|
|||
results.setSittingPoints(geometry->sittingPoints);
|
||||
Extents meshExtents = geometry->getUnscaledMeshExtents();
|
||||
results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
|
||||
results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -155,9 +155,9 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() {
|
|||
float yMin = std::min(yApex, yEnd);
|
||||
|
||||
// times 2 because dimensions are diameters not radii.
|
||||
glm::vec3 dims(2.0f * std::max(fabs(xMin), fabs(xMax)),
|
||||
2.0f * std::max(fabs(yMin), fabs(yMax)),
|
||||
2.0f * std::max(fabs(zMin), fabs(zMax)));
|
||||
glm::vec3 dims(2.0f * std::max(fabsf(xMin), fabsf(xMax)),
|
||||
2.0f * std::max(fabsf(yMin), fabsf(yMax)),
|
||||
2.0f * std::max(fabsf(zMin), fabsf(zMax)));
|
||||
|
||||
EntityItem::setDimensions(dims);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue