mirror of
https://github.com/overte-org/overte.git
synced 2025-04-17 05:30:41 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into ambient
This commit is contained in:
commit
df54ad2339
36 changed files with 569 additions and 243 deletions
|
@ -23,11 +23,13 @@ Item {
|
|||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property bool isDesktop: false
|
||||
property bool removingPage: false
|
||||
property bool loadingPage: false
|
||||
property string initialPage: ""
|
||||
property bool startingUp: true
|
||||
property alias webView: webview
|
||||
property alias profile: webview.profile
|
||||
property bool remove: false
|
||||
property var urlList: []
|
||||
property var forwardList: []
|
||||
|
||||
|
||||
property int currentPage: -1 // used as a model for repeater
|
||||
|
@ -79,9 +81,12 @@ Item {
|
|||
id: displayUrl
|
||||
color: hifi.colors.baseGray
|
||||
font.pixelSize: 12
|
||||
verticalAlignment: Text.AlignLeft
|
||||
anchors {
|
||||
top: nav.bottom
|
||||
horizontalCenter: parent.horizontalCenter;
|
||||
left: parent.left
|
||||
leftMargin: 20
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,19 +111,19 @@ Item {
|
|||
}
|
||||
|
||||
function goBack() {
|
||||
if (webview.canGoBack && !isUrlLoaded(webview.url)) {
|
||||
if (currentPage > 0) {
|
||||
removingPage = true;
|
||||
pagesModel.remove(currentPage);
|
||||
}
|
||||
if (webview.canGoBack) {
|
||||
forwardList.push(webview.url);
|
||||
webview.goBack();
|
||||
} else if (currentPage > 0) {
|
||||
removingPage = true;
|
||||
pagesModel.remove(currentPage);
|
||||
} else if (web.urlList.length > 0) {
|
||||
var url = web.urlList.pop();
|
||||
loadUrl(url);
|
||||
} else if (web.forwardList.length > 0) {
|
||||
var url = web.forwardList.pop();
|
||||
loadUrl(url);
|
||||
web.forwardList = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function closeWebEngine() {
|
||||
if (remove) {
|
||||
web.destroy();
|
||||
|
@ -151,32 +156,42 @@ Item {
|
|||
view.setEnabled(true);
|
||||
}
|
||||
|
||||
function loadUrl(url) {
|
||||
webview.url = url
|
||||
web.url = webview.url;
|
||||
web.address = webview.url;
|
||||
}
|
||||
|
||||
function onInitialPage(url) {
|
||||
return (url === webview.url);
|
||||
}
|
||||
|
||||
|
||||
function urlAppend(url) {
|
||||
if (removingPage) {
|
||||
removingPage = false;
|
||||
return;
|
||||
}
|
||||
var lurl = decodeURIComponent(url)
|
||||
if (lurl[lurl.length - 1] !== "/") {
|
||||
lurl = lurl + "/"
|
||||
}
|
||||
if (currentPage === -1 || (pagesModel.get(currentPage).webUrl !== lurl && !timer.running)) {
|
||||
timer.start();
|
||||
pagesModel.append({webUrl: lurl});
|
||||
}
|
||||
web.urlList.push(url);
|
||||
setBackButtonStatus();
|
||||
}
|
||||
|
||||
onCurrentPageChanged: {
|
||||
if (currentPage >= 0 && currentPage < pagesModel.count) {
|
||||
timer.start();
|
||||
webview.url = pagesModel.get(currentPage).webUrl;
|
||||
web.url = webview.url;
|
||||
web.address = webview.url;
|
||||
function setBackButtonStatus() {
|
||||
if (web.urlList.length > 0 || webview.canGoBack) {
|
||||
back.enabledColor = hifi.colors.darkGray;
|
||||
back.enabled = true;
|
||||
} else {
|
||||
back.enabledColor = hifi.colors.baseGray;
|
||||
back.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
onUrlChanged: {
|
||||
gotoPage(url)
|
||||
loadUrl(url);
|
||||
if (startingUp) {
|
||||
web.initialPage = webview.url;
|
||||
startingUp = false;
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
|
@ -184,18 +199,7 @@ Item {
|
|||
WebChannel.id: "eventBridgeWrapper"
|
||||
property var eventBridge;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 200
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: timer.stop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
WebEngineView {
|
||||
id: webview
|
||||
objectName: "webEngineView"
|
||||
|
@ -235,6 +239,7 @@ Item {
|
|||
worldId: WebEngineScript.MainWorld
|
||||
}
|
||||
|
||||
property string urlTag: "noDownload=false";
|
||||
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||
|
||||
property string newUrl: ""
|
||||
|
@ -261,9 +266,7 @@ Item {
|
|||
keyboard.resetShiftMode(false);
|
||||
// Required to support clicking on "hifi://" links
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
urlAppend(loadRequest.url.toString());
|
||||
loadingPage = true;
|
||||
var url = loadRequest.url.toString();
|
||||
var url = loadRequest.url.toString();
|
||||
if (urlHandler.canHandleUrl(url)) {
|
||||
if (urlHandler.handleUrl(url)) {
|
||||
root.stop();
|
||||
|
@ -274,9 +277,19 @@ Item {
|
|||
if (WebEngineView.LoadFailedStatus == loadRequest.status) {
|
||||
console.log(" Tablet WebEngineView failed to laod url: " + loadRequest.url.toString());
|
||||
}
|
||||
}
|
||||
|
||||
if (WebEngineView.LoadSucceededStatus == loadRequest.status) {
|
||||
web.address = webview.url;
|
||||
if (startingUp) {
|
||||
web.initialPage = webview.url;
|
||||
startingUp = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onNewViewRequested: {
|
||||
var currentUrl = webview.url;
|
||||
urlAppend(currentUrl);
|
||||
request.openIn(webview);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5423,9 +5423,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
entityScriptingInterface->setPacketSender(&_entityEditSender);
|
||||
entityScriptingInterface->setEntityTree(getEntities()->getTree());
|
||||
|
||||
// AvatarManager has some custom types
|
||||
AvatarManager::registerMetaTypes(scriptEngine);
|
||||
|
||||
// give the script engine to the RecordingScriptingInterface for its callbacks
|
||||
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(scriptEngine);
|
||||
|
||||
|
|
|
@ -13,9 +13,8 @@
|
|||
|
||||
#include "Camera.h"
|
||||
|
||||
#include <EntityItem.h>
|
||||
#include <EntityTypes.h>
|
||||
|
||||
// TODO: come up with a better name than "FancyCamera"
|
||||
class FancyCamera : public Camera {
|
||||
Q_OBJECT
|
||||
|
||||
|
|
|
@ -495,12 +495,15 @@ Menu::Menu() {
|
|||
qApp, SLOT(setActiveEyeTracker()));
|
||||
#endif
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
|
||||
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
|
||||
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false);
|
||||
connect(action, &QAction::triggered, [this]{ Avatar::setShowReceiveStats(isOptionChecked(MenuOption::AvatarReceiveStats)); });
|
||||
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowBoundingCollisionShapes, 0, false);
|
||||
connect(action, &QAction::triggered, [this]{ Avatar::setShowCollisionShapes(isOptionChecked(MenuOption::ShowBoundingCollisionShapes)); });
|
||||
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowMyLookAtVectors, 0, false);
|
||||
connect(action, &QAction::triggered, [this]{ Avatar::setShowMyLookAtVectors(isOptionChecked(MenuOption::ShowMyLookAtVectors)); });
|
||||
action = addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowOtherLookAtVectors, 0, false);
|
||||
connect(action, &QAction::triggered, [this]{ Avatar::setShowOtherLookAtVectors(isOptionChecked(MenuOption::ShowOtherLookAtVectors)); });
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderMyLookAtVectors, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderOtherLookAtVectors, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawDefaultPose(bool)));
|
||||
|
|
|
@ -145,9 +145,6 @@ namespace MenuOption {
|
|||
const QString Quit = "Quit";
|
||||
const QString ReloadAllScripts = "Reload All Scripts";
|
||||
const QString ReloadContent = "Reload Content (Clears all caches)";
|
||||
const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes";
|
||||
const QString RenderMyLookAtVectors = "Show My Eye Vectors";
|
||||
const QString RenderOtherLookAtVectors = "Show Other Eye Vectors";
|
||||
const QString RenderMaxTextureMemory = "Maximum Texture Memory";
|
||||
const QString RenderMaxTextureAutomatic = "Automatic Texture Memory";
|
||||
const QString RenderMaxTexture4MB = "4 MB";
|
||||
|
@ -173,8 +170,11 @@ namespace MenuOption {
|
|||
const QString SendWrongDSConnectVersion = "Send wrong DS connect version";
|
||||
const QString SendWrongProtocolVersion = "Send wrong protocol version";
|
||||
const QString SetHomeLocation = "Set Home Location";
|
||||
const QString ShowDSConnectTable = "Show Domain Connection Timing";
|
||||
const QString ShowBordersEntityNodes = "Show Entity Nodes";
|
||||
const QString ShowBoundingCollisionShapes = "Show Bounding Collision Shapes";
|
||||
const QString ShowDSConnectTable = "Show Domain Connection Timing";
|
||||
const QString ShowMyLookAtVectors = "Show My Eye Vectors";
|
||||
const QString ShowOtherLookAtVectors = "Show Other Eye Vectors";
|
||||
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
|
||||
const QString StandingHMDSensorMode = "Standing HMD Sensor Mode";
|
||||
const QString SimulateEyeTracking = "Simulate";
|
||||
|
|
|
@ -28,10 +28,8 @@
|
|||
#include <VariantMapToScriptValue.h>
|
||||
#include <DebugDraw.h>
|
||||
|
||||
#include "AvatarManager.h"
|
||||
#include "AvatarMotionState.h"
|
||||
#include "Camera.h"
|
||||
#include "Menu.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "SceneScriptingInterface.h"
|
||||
#include "SoftAttachmentModel.h"
|
||||
|
@ -59,7 +57,6 @@ namespace render {
|
|||
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
|
||||
if (avatarPtr->isInitialized() && args) {
|
||||
PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload");
|
||||
// TODO AVATARS_RENDERER: remove need for qApp
|
||||
avatarPtr->render(args);
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +71,31 @@ namespace render {
|
|||
}
|
||||
}
|
||||
|
||||
static bool showReceiveStats = false;
|
||||
void Avatar::setShowReceiveStats(bool receiveStats) {
|
||||
showReceiveStats = receiveStats;
|
||||
}
|
||||
|
||||
static bool showMyLookAtVectors = false;
|
||||
void Avatar::setShowMyLookAtVectors(bool showMine) {
|
||||
showMyLookAtVectors = showMine;
|
||||
}
|
||||
|
||||
static bool showOtherLookAtVectors = false;
|
||||
void Avatar::setShowOtherLookAtVectors(bool showOthers) {
|
||||
showOtherLookAtVectors = showOthers;
|
||||
}
|
||||
|
||||
static bool showCollisionShapes = false;
|
||||
void Avatar::setShowCollisionShapes(bool render) {
|
||||
showCollisionShapes = render;
|
||||
}
|
||||
|
||||
static bool showNamesAboveHeads = false;
|
||||
void Avatar::setShowNamesAboveHeads(bool show) {
|
||||
showNamesAboveHeads = show;
|
||||
}
|
||||
|
||||
Avatar::Avatar(QThread* thread, RigPointer rig) :
|
||||
AvatarData(),
|
||||
_skeletonOffset(0.0f),
|
||||
|
@ -126,11 +148,6 @@ Avatar::~Avatar() {
|
|||
});
|
||||
}
|
||||
|
||||
if (_motionState) {
|
||||
delete _motionState;
|
||||
_motionState = nullptr;
|
||||
}
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
geometryCache->releaseID(_nameRectGeometryID);
|
||||
|
@ -350,7 +367,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
|||
_smoothPositionTimer += deltaTime;
|
||||
if (_smoothPositionTimer < _smoothPositionTime) {
|
||||
AvatarData::setPosition(
|
||||
lerp(_smoothPositionInitial,
|
||||
lerp(_smoothPositionInitial,
|
||||
_smoothPositionTarget,
|
||||
easeInOutQuad(glm::clamp(_smoothPositionTimer / _smoothPositionTime, 0.0f, 1.0f)))
|
||||
);
|
||||
|
@ -363,7 +380,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
|||
_smoothOrientationTimer += deltaTime;
|
||||
if (_smoothOrientationTimer < _smoothOrientationTime) {
|
||||
AvatarData::setOrientation(
|
||||
slerp(_smoothOrientationInitial,
|
||||
slerp(_smoothOrientationInitial,
|
||||
_smoothOrientationTarget,
|
||||
easeInOutQuad(glm::clamp(_smoothOrientationTimer / _smoothOrientationTime, 0.0f, 1.0f)))
|
||||
);
|
||||
|
@ -535,14 +552,7 @@ void Avatar::updateRenderItem(render::Transaction& transaction) {
|
|||
|
||||
void Avatar::postUpdate(float deltaTime) {
|
||||
|
||||
bool renderLookAtVectors;
|
||||
if (isMyAvatar()) {
|
||||
renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderMyLookAtVectors);
|
||||
} else {
|
||||
renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderOtherLookAtVectors);
|
||||
}
|
||||
|
||||
if (renderLookAtVectors) {
|
||||
if (isMyAvatar() ? showMyLookAtVectors : showOtherLookAtVectors) {
|
||||
const float EYE_RAY_LENGTH = 10.0;
|
||||
const glm::vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
@ -583,7 +593,6 @@ void Avatar::render(RenderArgs* renderArgs) {
|
|||
bool havePosition, haveRotation;
|
||||
|
||||
if (_handState & LEFT_HAND_POINTING_FLAG) {
|
||||
|
||||
if (_handState & IS_FINGER_POINTING_FLAG) {
|
||||
int leftIndexTip = getJointIndex("LeftHandIndex4");
|
||||
int leftIndexTipJoint = getJointIndex("LeftHandIndex3");
|
||||
|
@ -636,44 +645,25 @@ void Avatar::render(RenderArgs* renderArgs) {
|
|||
return;
|
||||
}
|
||||
|
||||
glm::vec3 toTarget = frustum.getPosition() - getPosition();
|
||||
float distanceToTarget = glm::length(toTarget);
|
||||
fixupModelsInScene(renderArgs->_scene);
|
||||
|
||||
{
|
||||
fixupModelsInScene(renderArgs->_scene);
|
||||
|
||||
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) {
|
||||
// add local lights
|
||||
const float BASE_LIGHT_DISTANCE = 2.0f;
|
||||
const float LIGHT_FALLOFF_RADIUS = 0.01f;
|
||||
const float LIGHT_EXPONENT = 1.0f;
|
||||
const float LIGHT_CUTOFF = glm::radians(80.0f);
|
||||
float distance = BASE_LIGHT_DISTANCE * getUniformScale();
|
||||
glm::vec3 position = _skeletonModel->getTranslation();
|
||||
glm::quat orientation = getOrientation();
|
||||
foreach (const AvatarManager::LocalLight& light, DependencyManager::get<AvatarManager>()->getLocalLights()) {
|
||||
glm::vec3 direction = orientation * light.direction;
|
||||
DependencyManager::get<DeferredLightingEffect>()->addSpotLight(position - direction * distance,
|
||||
distance * 2.0f, light.color, 0.5f, LIGHT_FALLOFF_RADIUS, orientation, LIGHT_EXPONENT, LIGHT_CUTOFF);
|
||||
}
|
||||
}
|
||||
|
||||
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
|
||||
if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) {
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes");
|
||||
const float BOUNDING_SHAPE_ALPHA = 0.7f;
|
||||
_skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA);
|
||||
}
|
||||
if (showCollisionShapes && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) {
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes");
|
||||
const float BOUNDING_SHAPE_ALPHA = 0.7f;
|
||||
_skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA);
|
||||
}
|
||||
|
||||
const float DISPLAYNAME_DISTANCE = 20.0f;
|
||||
setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE);
|
||||
|
||||
if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) {
|
||||
auto& frustum = renderArgs->getViewFrustum();
|
||||
auto textPosition = getDisplayNamePosition();
|
||||
if (frustum.pointIntersectsFrustum(textPosition)) {
|
||||
renderDisplayName(batch, frustum, textPosition);
|
||||
if (showReceiveStats || showNamesAboveHeads) {
|
||||
glm::vec3 toTarget = frustum.getPosition() - getPosition();
|
||||
float distanceToTarget = glm::length(toTarget);
|
||||
const float DISPLAYNAME_DISTANCE = 20.0f;
|
||||
updateDisplayNameAlpha(distanceToTarget < DISPLAYNAME_DISTANCE);
|
||||
if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) {
|
||||
auto& frustum = renderArgs->getViewFrustum();
|
||||
auto textPosition = getDisplayNamePosition();
|
||||
if (frustum.pointIntersectsFrustum(textPosition)) {
|
||||
renderDisplayName(batch, frustum, textPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -829,7 +819,7 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& view, const g
|
|||
void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const {
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
|
||||
|
||||
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats() && !isMyAvatar();
|
||||
bool shouldShowReceiveStats = showReceiveStats && !isMyAvatar();
|
||||
|
||||
// If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return
|
||||
static const float CLIP_DISTANCE = 0.2f;
|
||||
|
@ -1200,8 +1190,8 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
|
||||
const float MOVE_DISTANCE_THRESHOLD = 0.001f;
|
||||
_moving = glm::distance(oldPosition, getPosition()) > MOVE_DISTANCE_THRESHOLD;
|
||||
if (_moving && _motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
|
||||
if (_moving) {
|
||||
addPhysicsFlags(Simulation::DIRTY_POSITION);
|
||||
}
|
||||
if (_moving || _hasNewJointData) {
|
||||
locationChanged();
|
||||
|
@ -1284,8 +1274,8 @@ float Avatar::getPelvisFloatingHeight() const {
|
|||
return -_skeletonModel->getBindExtents().minimum.y;
|
||||
}
|
||||
|
||||
void Avatar::setShowDisplayName(bool showDisplayName) {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::NamesAboveHeads)) {
|
||||
void Avatar::updateDisplayNameAlpha(bool showDisplayName) {
|
||||
if (!(showNamesAboveHeads || showReceiveStats)) {
|
||||
_displayNameAlpha = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
@ -1323,14 +1313,18 @@ void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
|
|||
radius = halfExtents.x;
|
||||
}
|
||||
|
||||
void Avatar::setMotionState(AvatarMotionState* motionState) {
|
||||
_motionState = motionState;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Avatar::rebuildCollisionShape() {
|
||||
if (_motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_SHAPE);
|
||||
addPhysicsFlags(Simulation::DIRTY_SHAPE);
|
||||
}
|
||||
|
||||
void Avatar::setPhysicsCallback(AvatarPhysicsCallback cb) {
|
||||
_physicsCallback = cb;
|
||||
}
|
||||
|
||||
void Avatar::addPhysicsFlags(uint32_t flags) {
|
||||
if (_physicsCallback) {
|
||||
_physicsCallback(flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1493,16 +1487,16 @@ QList<QVariant> Avatar::getSkeleton() {
|
|||
|
||||
void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) {
|
||||
if (scene) {
|
||||
render::Transaction transaction;
|
||||
auto nodelist = DependencyManager::get<NodeList>();
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()
|
||||
&& !nodelist->isIgnoringNode(getSessionUUID())
|
||||
&& !nodelist->isRadiusIgnoringNode(getSessionUUID())) {
|
||||
render::Transaction transaction;
|
||||
addToScene(myHandle, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
} else {
|
||||
qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown";
|
||||
qCWarning(interfaceapp) << "Avatar::addAvatar() : Unexpected null scene, possibly during application shutdown";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#ifndef hifi_Avatar_h
|
||||
#define hifi_Avatar_h
|
||||
|
||||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
|
@ -48,9 +49,10 @@ enum ScreenTintLayer {
|
|||
NUM_SCREEN_TINT_LAYERS
|
||||
};
|
||||
|
||||
class AvatarMotionState;
|
||||
class Texture;
|
||||
|
||||
using AvatarPhysicsCallback = std::function<void(uint32_t)>;
|
||||
|
||||
class Avatar : public AvatarData {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -66,6 +68,12 @@ class Avatar : public AvatarData {
|
|||
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
|
||||
|
||||
public:
|
||||
static void setShowReceiveStats(bool receiveStats);
|
||||
static void setShowMyLookAtVectors(bool showMine);
|
||||
static void setShowOtherLookAtVectors(bool showOthers);
|
||||
static void setShowCollisionShapes(bool render);
|
||||
static void setShowNamesAboveHeads(bool show);
|
||||
|
||||
explicit Avatar(QThread* thread, RigPointer rig = nullptr);
|
||||
~Avatar();
|
||||
|
||||
|
@ -148,7 +156,7 @@ public:
|
|||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
|
||||
|
||||
void setShowDisplayName(bool showDisplayName);
|
||||
void updateDisplayNameAlpha(bool showDisplayName);
|
||||
virtual void setSessionDisplayName(const QString& sessionDisplayName) override { }; // no-op
|
||||
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
|
||||
|
@ -185,8 +193,6 @@ public:
|
|||
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
|
||||
void getCapsule(glm::vec3& start, glm::vec3& end, float& radius);
|
||||
|
||||
AvatarMotionState* getMotionState() { return _motionState; }
|
||||
|
||||
using SpatiallyNestable::setPosition;
|
||||
virtual void setPosition(const glm::vec3& position) override;
|
||||
using SpatiallyNestable::setOrientation;
|
||||
|
@ -238,6 +244,17 @@ public:
|
|||
|
||||
return (lerpValue*(4.0f - 2.0f * lerpValue) - 1.0f);
|
||||
}
|
||||
float getBoundingRadius() const;
|
||||
|
||||
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
|
||||
bool isMoving() const { return _moving; }
|
||||
|
||||
//void setMotionState(AvatarMotionState* motionState);
|
||||
void setPhysicsCallback(AvatarPhysicsCallback cb);
|
||||
void addPhysicsFlags(uint32_t flags);
|
||||
bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; }
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -251,7 +268,6 @@ public slots:
|
|||
void setModelURLFinished(bool success);
|
||||
|
||||
protected:
|
||||
friend class AvatarManager;
|
||||
|
||||
const float SMOOTH_TIME_POSITION = 0.125f;
|
||||
const float SMOOTH_TIME_ORIENTATION = 0.075f;
|
||||
|
@ -260,8 +276,6 @@ protected:
|
|||
QString _empty{};
|
||||
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter!
|
||||
|
||||
void setMotionState(AvatarMotionState* motionState);
|
||||
|
||||
SkeletonModelPointer _skeletonModel;
|
||||
glm::vec3 _skeletonOffset;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentModels;
|
||||
|
@ -315,10 +329,6 @@ protected:
|
|||
ThreadSafeValueCache<glm::vec3> _rightPalmPositionCache { glm::vec3() };
|
||||
ThreadSafeValueCache<glm::quat> _rightPalmRotationCache { glm::quat() };
|
||||
|
||||
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
|
||||
|
||||
// Some rate tracking support
|
||||
RateCounter<> _simulationRate;
|
||||
RateCounter<> _simulationInViewRate;
|
||||
|
@ -354,13 +364,15 @@ private:
|
|||
bool _isLookAtTarget { false };
|
||||
bool _isAnimatingScale { false };
|
||||
|
||||
float getBoundingRadius() const;
|
||||
|
||||
static int _jointConesID;
|
||||
|
||||
int _voiceSphereID;
|
||||
|
||||
AvatarMotionState* _motionState = nullptr;
|
||||
AvatarPhysicsCallback _physicsCallback { nullptr };
|
||||
|
||||
float _displayNameTargetAlpha { 1.0f };
|
||||
float _displayNameAlpha { 1.0f };
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_Avatar_h
|
||||
|
|
|
@ -50,23 +50,6 @@ static const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND /
|
|||
// We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key.
|
||||
const QUuid MY_AVATAR_KEY; // NULL key
|
||||
|
||||
static QScriptValue localLightToScriptValue(QScriptEngine* engine, const AvatarManager::LocalLight& light) {
|
||||
QScriptValue object = engine->newObject();
|
||||
object.setProperty("direction", vec3toScriptValue(engine, light.direction));
|
||||
object.setProperty("color", vec3toScriptValue(engine, light.color));
|
||||
return object;
|
||||
}
|
||||
|
||||
static void localLightFromScriptValue(const QScriptValue& value, AvatarManager::LocalLight& light) {
|
||||
vec3FromScriptValue(value.property("direction"), light.direction);
|
||||
vec3FromScriptValue(value.property("color"), light.color);
|
||||
}
|
||||
|
||||
void AvatarManager::registerMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterMetaType(engine, localLightToScriptValue, localLightFromScriptValue);
|
||||
qScriptRegisterSequenceMetaType<QVector<AvatarManager::LocalLight> >(engine);
|
||||
}
|
||||
|
||||
AvatarManager::AvatarManager(QObject* parent) :
|
||||
_avatarsToFade(),
|
||||
_myAvatar(std::make_shared<MyAvatar>(qApp->thread(), std::make_shared<Rig>()))
|
||||
|
@ -91,6 +74,7 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
}
|
||||
|
||||
AvatarManager::~AvatarManager() {
|
||||
assert(_motionStates.empty());
|
||||
}
|
||||
|
||||
void AvatarManager::init() {
|
||||
|
@ -145,10 +129,9 @@ float AvatarManager::getAvatarUpdateRate(const QUuid& sessionID, const QString&
|
|||
|
||||
float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName) const {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(getAvatarBySessionID(sessionID));
|
||||
return avatar ? avatar->getSimulationRate(rateName) : 0.0f;
|
||||
return avatar ? avatar->getSimulationRate(rateName) : 0.0f;
|
||||
}
|
||||
|
||||
|
||||
void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||
// lock the hash for read to check the size
|
||||
QReadLocker lock(&_hashLock);
|
||||
|
@ -200,16 +183,15 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
if (_shouldRender) {
|
||||
avatar->ensureInScene(avatar, qApp->getMain3DScene());
|
||||
}
|
||||
if (!avatar->getMotionState()) {
|
||||
if (!avatar->isInPhysicsSimulation()) {
|
||||
ShapeInfo shapeInfo;
|
||||
avatar->computeShapeInfo(shapeInfo);
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
// don't add to the simulation now, instead put it on a list to be added later
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar.get(), shape);
|
||||
avatar->setMotionState(motionState);
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
avatar->setPhysicsCallback([=] (uint32_t flags) { motionState->addDirtyFlags(flags); });
|
||||
_motionStates.insert(avatar.get(), motionState);
|
||||
_motionStatesToAddToPhysics.insert(motionState);
|
||||
_motionStatesThatMightUpdate.insert(motionState);
|
||||
}
|
||||
}
|
||||
avatar->animateScaleChanges(deltaTime);
|
||||
|
@ -294,30 +276,24 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
const float MIN_FADE_SCALE = MIN_AVATAR_SCALE;
|
||||
|
||||
QReadLocker locker(&_hashLock);
|
||||
QVector<AvatarSharedPointer>::iterator itr = _avatarsToFade.begin();
|
||||
while (itr != _avatarsToFade.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*itr);
|
||||
QVector<AvatarSharedPointer>::iterator avatarItr = _avatarsToFade.begin();
|
||||
while (avatarItr != _avatarsToFade.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*avatarItr);
|
||||
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
|
||||
avatar->animateScaleChanges(deltaTime);
|
||||
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
|
||||
// fading to zero is such a rare event we push unique transaction for each one
|
||||
// fading to zero is such a rare event we push a unique transaction for each
|
||||
if (avatar->isInScene()) {
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(*itr, scene, transaction);
|
||||
avatar->removeFromScene(*avatarItr, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
// only remove from _avatarsToFade if we're sure its motionState has been removed from PhysicsEngine
|
||||
if (_motionStatesToRemoveFromPhysics.empty()) {
|
||||
itr = _avatarsToFade.erase(itr);
|
||||
} else {
|
||||
++itr;
|
||||
}
|
||||
avatarItr = _avatarsToFade.erase(avatarItr);
|
||||
} else {
|
||||
const bool inView = true; // HACK
|
||||
avatar->simulate(deltaTime, inView);
|
||||
++itr;
|
||||
++avatarItr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -329,15 +305,15 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
|||
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
|
||||
AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason);
|
||||
|
||||
// removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar
|
||||
// class in this context so we can call methods that don't exist at the base class.
|
||||
// remove from physics
|
||||
auto avatar = std::static_pointer_cast<Avatar>(removedAvatar);
|
||||
|
||||
AvatarMotionState* motionState = avatar->getMotionState();
|
||||
if (motionState) {
|
||||
_motionStatesThatMightUpdate.remove(motionState);
|
||||
avatar->setPhysicsCallback(nullptr);
|
||||
AvatarMotionStateMap::iterator itr = _motionStates.find(avatar.get());
|
||||
if (itr != _motionStates.end()) {
|
||||
AvatarMotionState* motionState = *itr;
|
||||
_motionStatesToAddToPhysics.remove(motionState);
|
||||
_motionStatesToRemoveFromPhysics.push_back(motionState);
|
||||
_motionStates.erase(itr);
|
||||
}
|
||||
|
||||
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
|
||||
|
@ -373,11 +349,15 @@ void AvatarManager::clearOtherAvatars() {
|
|||
++avatarIterator;
|
||||
}
|
||||
}
|
||||
assert(scene);
|
||||
scene->enqueueTransaction(transaction);
|
||||
_myAvatar->clearLookAtTargetAvatar();
|
||||
}
|
||||
|
||||
void AvatarManager::deleteAllAvatars() {
|
||||
assert(_motionStates.empty()); // should have called clearOtherAvatars() before getting here
|
||||
deleteMotionStates();
|
||||
|
||||
QReadLocker locker(&_hashLock);
|
||||
AvatarHash::iterator avatarIterator = _avatarHash.begin();
|
||||
while (avatarIterator != _avatarHash.end()) {
|
||||
|
@ -387,27 +367,18 @@ void AvatarManager::deleteAllAvatars() {
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarManager::setLocalLights(const QVector<AvatarManager::LocalLight>& localLights) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setLocalLights", Q_ARG(const QVector<AvatarManager::LocalLight>&, localLights));
|
||||
return;
|
||||
void AvatarManager::deleteMotionStates() {
|
||||
// delete motionstates that were removed from physics last frame
|
||||
for (auto state : _motionStatesToDelete) {
|
||||
delete state;
|
||||
}
|
||||
_localLights = localLights;
|
||||
}
|
||||
|
||||
QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVector<AvatarManager::LocalLight> result;
|
||||
QMetaObject::invokeMethod(const_cast<AvatarManager*>(this), "getLocalLights", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QVector<AvatarManager::LocalLight>, result));
|
||||
return result;
|
||||
}
|
||||
return _localLights;
|
||||
_motionStatesToDelete.clear();
|
||||
}
|
||||
|
||||
void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
result.swap(_motionStatesToRemoveFromPhysics);
|
||||
deleteMotionStates();
|
||||
result = _motionStatesToRemoveFromPhysics;
|
||||
_motionStatesToDelete.swap(_motionStatesToRemoveFromPhysics);
|
||||
}
|
||||
|
||||
void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
||||
|
@ -420,10 +391,12 @@ void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
|||
|
||||
void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
for (auto state : _motionStatesThatMightUpdate) {
|
||||
if (state->_dirtyFlags > 0) {
|
||||
result.push_back(state);
|
||||
AvatarMotionStateMap::iterator motionStateItr = _motionStates.begin();
|
||||
while (motionStateItr != _motionStates.end()) {
|
||||
if ((*motionStateItr)->getIncomingDirtyFlags() != 0) {
|
||||
result.push_back(*motionStateItr);
|
||||
}
|
||||
++motionStateItr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,18 +62,6 @@ public:
|
|||
void clearOtherAvatars();
|
||||
void deleteAllAvatars();
|
||||
|
||||
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
|
||||
|
||||
class LocalLight {
|
||||
public:
|
||||
glm::vec3 color;
|
||||
glm::vec3 direction;
|
||||
};
|
||||
|
||||
Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
|
||||
Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
|
||||
|
||||
|
||||
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToChange(VectorOfMotionStates& motionStates);
|
||||
|
@ -95,7 +83,6 @@ public:
|
|||
float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); }
|
||||
|
||||
public slots:
|
||||
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
||||
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
||||
|
||||
private:
|
||||
|
@ -105,21 +92,20 @@ private:
|
|||
void simulateAvatarFades(float deltaTime);
|
||||
|
||||
AvatarSharedPointer newSharedAvatar() override;
|
||||
void deleteMotionStates();
|
||||
void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
|
||||
|
||||
QVector<AvatarSharedPointer> _avatarsToFade;
|
||||
|
||||
QSet<AvatarMotionState*> _motionStatesThatMightUpdate;
|
||||
using AvatarMotionStateMap = QMap<Avatar*, AvatarMotionState*>;
|
||||
AvatarMotionStateMap _motionStates;
|
||||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||
VectorOfMotionStates _motionStatesToDelete;
|
||||
SetOfMotionStates _motionStatesToAddToPhysics;
|
||||
|
||||
std::shared_ptr<MyAvatar> _myAvatar;
|
||||
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
|
||||
|
||||
QVector<AvatarManager::LocalLight> _localLights;
|
||||
|
||||
bool _shouldShowReceiveStats = false;
|
||||
|
||||
std::list<QPointer<AudioInjector>> _collisionInjectors;
|
||||
|
||||
RateCounter<> _myAvatarSendRate;
|
||||
|
@ -129,7 +115,4 @@ private:
|
|||
bool _shouldRender { true };
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
||||
Q_DECLARE_METATYPE(QVector<AvatarManager::LocalLight>)
|
||||
|
||||
#endif // hifi_AvatarManager_h
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "AvatarMotionState.h"
|
||||
#include "BulletUtil.h"
|
||||
|
||||
AvatarMotionState::AvatarMotionState(Avatar* avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||
AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||
assert(_avatar);
|
||||
_type = MOTIONSTATE_TYPE_AVATAR;
|
||||
if (_shape) {
|
||||
|
@ -49,7 +49,7 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
|
|||
// virtual and protected
|
||||
const btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeShapeInfo(shapeInfo);
|
||||
std::static_pointer_cast<Avatar>(_avatar)->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ glm::vec3 AvatarMotionState::getObjectAngularVelocity() const {
|
|||
|
||||
// virtual
|
||||
glm::vec3 AvatarMotionState::getObjectGravity() const {
|
||||
return _avatar->getAcceleration();
|
||||
return std::static_pointer_cast<Avatar>(_avatar)->getAcceleration();
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
|
|
@ -20,7 +20,7 @@ class Avatar;
|
|||
|
||||
class AvatarMotionState : public ObjectMotionState {
|
||||
public:
|
||||
AvatarMotionState(Avatar* avatar, const btCollisionShape* shape);
|
||||
AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape);
|
||||
|
||||
virtual PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||
|
||||
|
@ -74,11 +74,7 @@ protected:
|
|||
virtual bool isReadyToComputeShape() const override { return true; }
|
||||
virtual const btCollisionShape* computeNewShape() override;
|
||||
|
||||
// The AvatarMotionState keeps a RAW backpointer to its Avatar because all AvatarMotionState
|
||||
// instances are "owned" by their corresponding Avatar instance and are deleted in the Avatar dtor.
|
||||
// In other words, it is impossible for the Avatar to be deleted out from under its MotionState.
|
||||
// In conclusion: weak pointer shennanigans would be pure overhead.
|
||||
Avatar* _avatar; // do NOT use smartpointer here, no need for weakpointer
|
||||
AvatarSharedPointer _avatar;
|
||||
|
||||
uint32_t _dirtyFlags;
|
||||
};
|
||||
|
|
|
@ -63,8 +63,6 @@ AvatarData::AvatarData() :
|
|||
_keyState(NO_KEY_DOWN),
|
||||
_forceFaceTrackerConnected(false),
|
||||
_headData(NULL),
|
||||
_displayNameTargetAlpha(1.0f),
|
||||
_displayNameAlpha(1.0f),
|
||||
_errorLogExpiry(0),
|
||||
_owningAvatarMixer(),
|
||||
_targetVelocity(0.0f)
|
||||
|
|
|
@ -692,9 +692,6 @@ protected:
|
|||
QString _sessionDisplayName { };
|
||||
QUrl cannonicalSkeletonModelURL(const QUrl& empty) const;
|
||||
|
||||
float _displayNameTargetAlpha;
|
||||
float _displayNameAlpha;
|
||||
|
||||
QHash<QString, int> _jointIndices; ///< 1-based, since zero is returned for missing keys
|
||||
QStringList _jointNames; ///< in order of depth-first traversal
|
||||
|
||||
|
|
|
@ -69,5 +69,23 @@ namespace controller {
|
|||
pose.valid = valid;
|
||||
return pose;
|
||||
}
|
||||
|
||||
Pose Pose::postTransform(const glm::mat4& mat) const {
|
||||
glm::mat4 original = ::createMatFromQuatAndPos(rotation, translation);
|
||||
glm::mat4 result = original * mat;
|
||||
auto translationOut = ::extractTranslation(result);
|
||||
auto rotationOut = ::glmExtractRotation(result);
|
||||
auto velocityOut = velocity + glm::cross(angularVelocity, translationOut - translation); // warning: this may be completely wrong
|
||||
auto angularVelocityOut = angularVelocity;
|
||||
|
||||
Pose pose(translationOut,
|
||||
rotationOut,
|
||||
velocityOut,
|
||||
angularVelocityOut);
|
||||
|
||||
pose.valid = valid;
|
||||
return pose;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ namespace controller {
|
|||
vec3 getAngularVelocity() const { return angularVelocity; }
|
||||
|
||||
Pose transform(const glm::mat4& mat) const;
|
||||
Pose postTransform(const glm::mat4& mat) const;
|
||||
|
||||
static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event);
|
||||
static void fromScriptValue(const QScriptValue& object, Pose& event);
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <PathUtils.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include <StreamUtils.h>
|
||||
|
||||
#include "StandardController.h"
|
||||
#include "StateController.h"
|
||||
#include "InputRecorder.h"
|
||||
|
@ -563,7 +565,18 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) {
|
|||
if (source->isPose()) {
|
||||
Pose value = getPose(source, route->peek);
|
||||
static const Pose IDENTITY_POSE { vec3(), quat() };
|
||||
|
||||
if (debugRoutes && route->debug) {
|
||||
qCDebug(controllers) << "Value was t:" << value.translation << "r:" << value.rotation;
|
||||
}
|
||||
// Apply each of the filters.
|
||||
for (const auto& filter : route->filters) {
|
||||
value = filter->apply(value);
|
||||
}
|
||||
|
||||
if (debugRoutes && route->debug) {
|
||||
qCDebug(controllers) << "Filtered value was t:" << value.translation << "r:" << value.rotation;
|
||||
|
||||
if (!value.valid) {
|
||||
qCDebug(controllers) << "Applying invalid pose";
|
||||
} else if (value == IDENTITY_POSE) {
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
#include "filters/InvertFilter.h"
|
||||
#include "filters/PulseFilter.h"
|
||||
#include "filters/ScaleFilter.h"
|
||||
#include "filters/TranslateFilter.h"
|
||||
#include "filters/TransformFilter.h"
|
||||
#include "filters/PostTransformFilter.h"
|
||||
#include "filters/RotateFilter.h"
|
||||
|
||||
using namespace controller;
|
||||
|
||||
|
@ -37,6 +41,10 @@ REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis")
|
|||
REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(TranslateFilter, "translate")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(TransformFilter, "transform")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(PostTransformFilter, "postTransform")
|
||||
REGISTER_FILTER_CLASS_INSTANCE(RotateFilter, "rotate")
|
||||
|
||||
const QString JSON_FILTER_TYPE = QStringLiteral("type");
|
||||
const QString JSON_FILTER_PARAMS = QStringLiteral("params");
|
||||
|
@ -76,7 +84,6 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri
|
|||
return true;
|
||||
}
|
||||
} else if (parameters.isObject()) {
|
||||
static const QString JSON_MIN = QStringLiteral("interval");
|
||||
auto objectParameters = parameters.toObject();
|
||||
if (objectParameters.contains(name)) {
|
||||
output = objectParameters[name].toDouble();
|
||||
|
@ -86,6 +93,92 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Filter::parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output) {
|
||||
if (parameters.isDouble()) {
|
||||
output = glm::vec3(parameters.toDouble());
|
||||
return true;
|
||||
} else if (parameters.isArray()) {
|
||||
auto arrayParameters = parameters.toArray();
|
||||
if (arrayParameters.size() == 3) {
|
||||
output = glm::vec3(arrayParameters[0].toDouble(),
|
||||
arrayParameters[1].toDouble(),
|
||||
arrayParameters[2].toDouble());
|
||||
return true;
|
||||
}
|
||||
} else if (parameters.isObject()) {
|
||||
auto objectParameters = parameters.toObject();
|
||||
if (objectParameters.contains("x") && objectParameters.contains("y") && objectParameters.contains("z")) {
|
||||
output = glm::vec3(objectParameters["x"].toDouble(),
|
||||
objectParameters["y"].toDouble(),
|
||||
objectParameters["z"].toDouble());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Filter::parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output) {
|
||||
if (parameters.isObject()) {
|
||||
auto objectParameters = parameters.toObject();
|
||||
|
||||
|
||||
if (objectParameters.contains("r0c0") &&
|
||||
objectParameters.contains("r1c0") &&
|
||||
objectParameters.contains("r2c0") &&
|
||||
objectParameters.contains("r3c0") &&
|
||||
objectParameters.contains("r0c1") &&
|
||||
objectParameters.contains("r1c1") &&
|
||||
objectParameters.contains("r2c1") &&
|
||||
objectParameters.contains("r3c1") &&
|
||||
objectParameters.contains("r0c2") &&
|
||||
objectParameters.contains("r1c2") &&
|
||||
objectParameters.contains("r2c2") &&
|
||||
objectParameters.contains("r3c2") &&
|
||||
objectParameters.contains("r0c3") &&
|
||||
objectParameters.contains("r1c3") &&
|
||||
objectParameters.contains("r2c3") &&
|
||||
objectParameters.contains("r3c3")) {
|
||||
|
||||
output[0][0] = objectParameters["r0c0"].toDouble();
|
||||
output[0][1] = objectParameters["r1c0"].toDouble();
|
||||
output[0][2] = objectParameters["r2c0"].toDouble();
|
||||
output[0][3] = objectParameters["r3c0"].toDouble();
|
||||
output[1][0] = objectParameters["r0c1"].toDouble();
|
||||
output[1][1] = objectParameters["r1c1"].toDouble();
|
||||
output[1][2] = objectParameters["r2c1"].toDouble();
|
||||
output[1][3] = objectParameters["r3c1"].toDouble();
|
||||
output[2][0] = objectParameters["r0c2"].toDouble();
|
||||
output[2][1] = objectParameters["r1c2"].toDouble();
|
||||
output[2][2] = objectParameters["r2c2"].toDouble();
|
||||
output[2][3] = objectParameters["r3c2"].toDouble();
|
||||
output[3][0] = objectParameters["r0c3"].toDouble();
|
||||
output[3][1] = objectParameters["r1c3"].toDouble();
|
||||
output[3][2] = objectParameters["r2c3"].toDouble();
|
||||
output[3][3] = objectParameters["r3c3"].toDouble();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Filter::parseQuatParameter(const QJsonValue& parameters, glm::quat& output) {
|
||||
if (parameters.isObject()) {
|
||||
auto objectParameters = parameters.toObject();
|
||||
if (objectParameters.contains("w") &&
|
||||
objectParameters.contains("x") &&
|
||||
objectParameters.contains("y") &&
|
||||
objectParameters.contains("z")) {
|
||||
|
||||
output = glm::quat(objectParameters["w"].toDouble(),
|
||||
objectParameters["x"].toDouble(),
|
||||
objectParameters["y"].toDouble(),
|
||||
objectParameters["z"].toDouble());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include <QtCore/QEasingCurve>
|
||||
|
||||
#include "../Pose.h"
|
||||
|
||||
class QJsonValue;
|
||||
|
||||
namespace controller {
|
||||
|
@ -34,6 +36,8 @@ namespace controller {
|
|||
using Factory = hifi::SimpleFactory<Filter, QString>;
|
||||
|
||||
virtual float apply(float value) const = 0;
|
||||
virtual Pose apply(Pose value) const = 0;
|
||||
|
||||
// Factory features
|
||||
virtual bool parseParameters(const QJsonValue& parameters) { return true; }
|
||||
|
||||
|
@ -42,6 +46,9 @@ namespace controller {
|
|||
static Factory& getFactory() { return _factory; }
|
||||
|
||||
static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output);
|
||||
static bool parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output);
|
||||
static bool parseQuatParameter(const QJsonValue& parameters, glm::quat& output);
|
||||
static bool parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output);
|
||||
protected:
|
||||
static Factory _factory;
|
||||
};
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
#include "filters/InvertFilter.h"
|
||||
#include "filters/PulseFilter.h"
|
||||
#include "filters/ScaleFilter.h"
|
||||
#include "filters/TranslateFilter.h"
|
||||
#include "filters/TransformFilter.h"
|
||||
#include "filters/PostTransformFilter.h"
|
||||
#include "filters/RotateFilter.h"
|
||||
#include "conditionals/AndConditional.h"
|
||||
|
||||
using namespace controller;
|
||||
|
@ -103,6 +107,26 @@ QObject* RouteBuilderProxy::deadZone(float min) {
|
|||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::translate(glm::vec3 translate) {
|
||||
addFilter(std::make_shared<TranslateFilter>(translate));
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::transform(glm::mat4 transform) {
|
||||
addFilter(std::make_shared<TransformFilter>(transform));
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::postTransform(glm::mat4 transform) {
|
||||
addFilter(std::make_shared<PostTransformFilter>(transform));
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::rotate(glm::quat rotation) {
|
||||
addFilter(std::make_shared<RotateFilter>(rotation));
|
||||
return this;
|
||||
}
|
||||
|
||||
QObject* RouteBuilderProxy::constrainToInteger() {
|
||||
addFilter(std::make_shared<ConstrainToIntegerFilter>());
|
||||
return this;
|
||||
|
|
|
@ -48,6 +48,10 @@ class RouteBuilderProxy : public QObject {
|
|||
Q_INVOKABLE QObject* deadZone(float min);
|
||||
Q_INVOKABLE QObject* constrainToInteger();
|
||||
Q_INVOKABLE QObject* constrainToPositiveInteger();
|
||||
Q_INVOKABLE QObject* translate(glm::vec3 translate);
|
||||
Q_INVOKABLE QObject* transform(glm::mat4 transform);
|
||||
Q_INVOKABLE QObject* postTransform(glm::mat4 transform);
|
||||
Q_INVOKABLE QObject* rotate(glm::quat rotation);
|
||||
|
||||
private:
|
||||
void to(const Endpoint::Pointer& destination);
|
||||
|
|
|
@ -21,6 +21,9 @@ public:
|
|||
virtual float apply(float value) const override {
|
||||
return glm::clamp(value, _min, _max);
|
||||
}
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override;
|
||||
protected:
|
||||
float _min = 0.0f;
|
||||
|
|
|
@ -22,6 +22,9 @@ public:
|
|||
virtual float apply(float value) const override {
|
||||
return glm::sign(value);
|
||||
}
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ public:
|
|||
virtual float apply(float value) const override {
|
||||
return (value <= 0.0f) ? 0.0f : 1.0f;
|
||||
}
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ public:
|
|||
DeadZoneFilter(float min = 0.0) : _min(min) {};
|
||||
|
||||
virtual float apply(float value) const override;
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override;
|
||||
protected:
|
||||
float _min = 0.0f;
|
||||
|
|
|
@ -19,6 +19,9 @@ class HysteresisFilter : public Filter {
|
|||
public:
|
||||
HysteresisFilter(float min = 0.25, float max = 0.75);
|
||||
virtual float apply(float value) const override;
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override;
|
||||
protected:
|
||||
float _min;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Created by Brad Hefta-Gaub 2017/04/11
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_Controllers_Filters_PostTransform_h
|
||||
#define hifi_Controllers_Filters_PostTransform_h
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "../Filter.h"
|
||||
|
||||
namespace controller {
|
||||
|
||||
class PostTransformFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(PostTransformFilter);
|
||||
public:
|
||||
PostTransformFilter() { }
|
||||
PostTransformFilter(glm::mat4 transform) : _transform(transform) {}
|
||||
virtual float apply(float value) const override { return value; }
|
||||
virtual Pose apply(Pose value) const override { return value.postTransform(_transform); }
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); }
|
||||
private:
|
||||
glm::mat4 _transform;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -23,6 +23,8 @@ public:
|
|||
|
||||
virtual float apply(float value) const override;
|
||||
|
||||
virtual Pose apply(Pose value) const override { return value; }
|
||||
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Created by Brad Hefta-Gaub 2017/04/11
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_Controllers_Filters_Rotate_h
|
||||
#define hifi_Controllers_Filters_Rotate_h
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "../Filter.h"
|
||||
|
||||
namespace controller {
|
||||
|
||||
class RotateFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(RotateFilter);
|
||||
public:
|
||||
RotateFilter() { }
|
||||
RotateFilter(glm::quat rotation) : _rotation(rotation) {}
|
||||
|
||||
virtual float apply(float value) const override { return value; }
|
||||
|
||||
virtual Pose apply(Pose value) const override {
|
||||
return value.transform(glm::mat4(glm::quat(_rotation)));
|
||||
}
|
||||
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override { return parseQuatParameter(parameters, _rotation); }
|
||||
|
||||
private:
|
||||
glm::quat _rotation;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -10,6 +10,8 @@
|
|||
#ifndef hifi_Controllers_Filters_Scale_h
|
||||
#define hifi_Controllers_Filters_Scale_h
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "../Filter.h"
|
||||
|
||||
namespace controller {
|
||||
|
@ -23,6 +25,11 @@ public:
|
|||
virtual float apply(float value) const override {
|
||||
return value * _scale;
|
||||
}
|
||||
|
||||
virtual Pose apply(Pose value) const override {
|
||||
return value.transform(glm::scale(glm::mat4(), glm::vec3(_scale)));
|
||||
}
|
||||
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Created by Brad Hefta-Gaub 2017/04/11
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_Controllers_Filters_Transform_h
|
||||
#define hifi_Controllers_Filters_Transform_h
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "../Filter.h"
|
||||
|
||||
namespace controller {
|
||||
|
||||
class TransformFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(TransformFilter);
|
||||
public:
|
||||
TransformFilter() { }
|
||||
TransformFilter(glm::mat4 transform) : _transform(transform) {}
|
||||
|
||||
virtual float apply(float value) const override { return value; }
|
||||
virtual Pose apply(Pose value) const override { return value.transform(_transform); }
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); }
|
||||
|
||||
private:
|
||||
glm::mat4 _transform;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Created by Brad Hefta-Gaub 2017/04/11
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_Controllers_Filters_Translate_h
|
||||
#define hifi_Controllers_Filters_Translate_h
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "../Filter.h"
|
||||
|
||||
namespace controller {
|
||||
|
||||
class TranslateFilter : public Filter {
|
||||
REGISTER_FILTER_CLASS(TranslateFilter);
|
||||
public:
|
||||
TranslateFilter() { }
|
||||
TranslateFilter(glm::vec3 translate) : _translate(translate) {}
|
||||
|
||||
virtual float apply(float value) const override { return value; }
|
||||
virtual Pose apply(Pose value) const override { return value.transform(glm::translate(_translate)); }
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override { return parseVec3Parameter(parameters, _translate); }
|
||||
|
||||
private:
|
||||
glm::vec3 _translate { 0.0f };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -170,7 +170,7 @@ protected:
|
|||
bool _hasInternalKinematicChanges { false };
|
||||
};
|
||||
|
||||
typedef QSet<ObjectMotionState*> SetOfMotionStates;
|
||||
typedef QVector<ObjectMotionState*> VectorOfMotionStates;
|
||||
using SetOfMotionStates = QSet<ObjectMotionState*>;
|
||||
using VectorOfMotionStates = QVector<ObjectMotionState*>;
|
||||
|
||||
#endif // hifi_ObjectMotionState_h
|
||||
|
|
|
@ -220,6 +220,7 @@ void PhysicsEngine::removeObjects(const SetOfMotionStates& objects) {
|
|||
body->setMotionState(nullptr);
|
||||
delete body;
|
||||
}
|
||||
object->clearIncomingDirtyFlags();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@ public:
|
|||
void* _b; // ObjectMotionState pointer
|
||||
};
|
||||
|
||||
typedef std::map<ContactKey, ContactInfo> ContactMap;
|
||||
typedef std::vector<Collision> CollisionEvents;
|
||||
using ContactMap = std::map<ContactKey, ContactInfo>;
|
||||
using CollisionEvents = std::vector<Collision>;
|
||||
|
||||
class PhysicsEngine {
|
||||
public:
|
||||
|
|
|
@ -130,8 +130,8 @@ public:
|
|||
bool _enableTexturing { true };
|
||||
|
||||
RenderDetails _details;
|
||||
render::ScenePointer _scene; // HACK
|
||||
int8_t _cameraMode { -1 }; // HACK
|
||||
render::ScenePointer _scene;
|
||||
int8_t _cameraMode { -1 };
|
||||
};
|
||||
|
||||
#endif // hifi_RenderArgs_h
|
||||
|
|
34
script-archive/controllers/puppetFeet3.js
Normal file
34
script-archive/controllers/puppetFeet3.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// puppetFeet3.js
|
||||
// examples/controllers
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2017/04/11
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
|
||||
var MAPPING_NAME = "com.highfidelity.examples.puppetFeet3";
|
||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||
var puppetOffset = { x: 0, y: -1, z: 0 };
|
||||
|
||||
var rotation = Quat.fromPitchYawRollDegrees(0, 0, -90);
|
||||
var noTranslation = { x: 0, y: 0, z: 0 };
|
||||
var transformMatrix = Mat4.createFromRotAndTrans(rotation, noTranslation);
|
||||
var rotateAndTranslate = Mat4.createFromRotAndTrans(rotation, puppetOffset);
|
||||
|
||||
|
||||
mapping.from(Controller.Standard.LeftHand).peek().rotate(rotation).translate(puppetOffset).to(Controller.Standard.LeftFoot);
|
||||
|
||||
//mapping.from(Controller.Standard.LeftHand).peek().translate(puppetOffset).to(Controller.Standard.LeftFoot);
|
||||
//mapping.from(Controller.Standard.LeftHand).peek().transform(transformMatrix).translate(puppetOffset).to(Controller.Standard.LeftFoot);
|
||||
//mapping.from(Controller.Standard.LeftHand).peek().transform(rotateAndTranslate).to(Controller.Standard.LeftFoot);
|
||||
|
||||
Controller.enableMapping(MAPPING_NAME);
|
||||
|
||||
|
||||
Script.scriptEnding.connect(function(){
|
||||
mapping.disable();
|
||||
});
|
Loading…
Reference in a new issue