mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 19:36:45 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into audio-src
This commit is contained in:
commit
0ad01ca935
17 changed files with 616 additions and 383 deletions
|
@ -1744,13 +1744,26 @@ void DomainServer::addStaticAssignmentsToQueue() {
|
||||||
|
|
||||||
// if the domain-server has just restarted,
|
// if the domain-server has just restarted,
|
||||||
// check if there are static assignments that we need to throw into the assignment queue
|
// check if there are static assignments that we need to throw into the assignment queue
|
||||||
QHash<QUuid, SharedAssignmentPointer> staticHashCopy = _allAssignments;
|
auto sharedAssignments = _allAssignments.values();
|
||||||
QHash<QUuid, SharedAssignmentPointer>::iterator staticAssignment = staticHashCopy.begin();
|
|
||||||
while (staticAssignment != staticHashCopy.end()) {
|
// sort the assignments to put the server/mixer assignments first
|
||||||
|
qSort(sharedAssignments.begin(), sharedAssignments.end(), [](SharedAssignmentPointer a, SharedAssignmentPointer b){
|
||||||
|
if (a->getType() == b->getType()) {
|
||||||
|
return true;
|
||||||
|
} else if (a->getType() != Assignment::AgentType && b->getType() != Assignment::AgentType) {
|
||||||
|
return a->getType() < b->getType();
|
||||||
|
} else {
|
||||||
|
return a->getType() != Assignment::AgentType;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto staticAssignment = sharedAssignments.begin();
|
||||||
|
|
||||||
|
while (staticAssignment != sharedAssignments.end()) {
|
||||||
// add any of the un-matched static assignments to the queue
|
// add any of the un-matched static assignments to the queue
|
||||||
|
|
||||||
// enumerate the nodes and check if there is one with an attached assignment with matching UUID
|
// enumerate the nodes and check if there is one with an attached assignment with matching UUID
|
||||||
if (!DependencyManager::get<LimitedNodeList>()->nodeWithUUID(staticAssignment->data()->getUUID())) {
|
if (!DependencyManager::get<LimitedNodeList>()->nodeWithUUID((*staticAssignment)->getUUID())) {
|
||||||
// this assignment has not been fulfilled - reset the UUID and add it to the assignment queue
|
// this assignment has not been fulfilled - reset the UUID and add it to the assignment queue
|
||||||
refreshStaticAssignmentAndAddToQueue(*staticAssignment);
|
refreshStaticAssignmentAndAddToQueue(*staticAssignment);
|
||||||
}
|
}
|
||||||
|
|
98
examples/faceBlendCoefficients.js
Normal file
98
examples/faceBlendCoefficients.js
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
//
|
||||||
|
// faceBlendCoefficients.js
|
||||||
|
//
|
||||||
|
// version 2.0
|
||||||
|
//
|
||||||
|
// Created by Bob Long, 9/14/2015
|
||||||
|
// A simple panel that can select and display the blending coefficient of the Avatar's face model.
|
||||||
|
//
|
||||||
|
// 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('utilities/tools/cookies.js')
|
||||||
|
|
||||||
|
var panel;
|
||||||
|
var coeff;
|
||||||
|
var interval;
|
||||||
|
var item = 0;
|
||||||
|
var DEVELOPER_MENU = "Developer";
|
||||||
|
var AVATAR_MENU = DEVELOPER_MENU + " > Avatar";
|
||||||
|
var SHOW_FACE_BLEND_COEFFICIENTS = "Show face blend coefficients"
|
||||||
|
|
||||||
|
function MenuConnect(menuItem) {
|
||||||
|
if (menuItem == SHOW_FACE_BLEND_COEFFICIENTS) {
|
||||||
|
if(Menu.isOptionChecked(SHOW_FACE_BLEND_COEFFICIENTS)) {
|
||||||
|
panel.show();
|
||||||
|
Overlays.editOverlay(coeff, { visible : true });
|
||||||
|
} else {
|
||||||
|
panel.hide();
|
||||||
|
Overlays.editOverlay(coeff, { visible : false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a menu item to show/hide the coefficients
|
||||||
|
function setupMenu() {
|
||||||
|
if (!Menu.menuExists(DEVELOPER_MENU)) {
|
||||||
|
Menu.addMenu(DEVELOPER_MENU);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Menu.menuExists(AVATAR_MENU)) {
|
||||||
|
Menu.addMenu(AVATAR_MENU);
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu.addMenuItem({ menuName: AVATAR_MENU, menuItemName: SHOW_FACE_BLEND_COEFFICIENTS, isCheckable: true, isChecked: true });
|
||||||
|
Menu.menuItemEvent.connect(MenuConnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupPanel() {
|
||||||
|
panel = new Panel(10, 400);
|
||||||
|
|
||||||
|
// Slider to select which coefficient to display
|
||||||
|
panel.newSlider("Select Coefficient Index",
|
||||||
|
0,
|
||||||
|
100,
|
||||||
|
function(value) { item = value.toFixed(0); },
|
||||||
|
function() { return item; },
|
||||||
|
function(value) { return "index = " + item; }
|
||||||
|
);
|
||||||
|
|
||||||
|
// The raw overlay used to show the actual coefficient value
|
||||||
|
coeff = Overlays.addOverlay("text", {
|
||||||
|
x: 10,
|
||||||
|
y: 420,
|
||||||
|
width: 300,
|
||||||
|
height: 50,
|
||||||
|
color: { red: 255, green: 255, blue: 255 },
|
||||||
|
alpha: 1.0,
|
||||||
|
backgroundColor: { red: 127, green: 127, blue: 127 },
|
||||||
|
backgroundAlpha: 0.5,
|
||||||
|
topMargin: 15,
|
||||||
|
leftMargin: 20,
|
||||||
|
text: "Coefficient: 0.0"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up the interval (0.5 sec) to update the coefficient.
|
||||||
|
interval = Script.setInterval(function() {
|
||||||
|
Overlays.editOverlay(coeff, { text: "Coefficient: " + MyAvatar.getFaceBlendCoef(item).toFixed(4) });
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Mouse event setup
|
||||||
|
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
|
||||||
|
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
|
||||||
|
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
function scriptEnding() {
|
||||||
|
panel.destroy();
|
||||||
|
Overlays.deleteOverlay(coeff);
|
||||||
|
Script.clearInterval(interval);
|
||||||
|
|
||||||
|
Menu.removeMenuItem(AVATAR_MENU, SHOW_FACE_BLEND_COEFFICIENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMenu();
|
||||||
|
setupPanel();
|
||||||
|
Script.scriptEnding.connect(scriptEnding);
|
|
@ -15,21 +15,24 @@
|
||||||
Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js");
|
Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js");
|
||||||
|
|
||||||
|
|
||||||
var scriptURL = "https://hifi-public.s3.amazonaws.com/scripts/toys/flashlight/flashlight.js?"+randInt(0,1000);
|
var scriptURL = Script.resolvePath('flashlight.js?123123');
|
||||||
|
|
||||||
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
||||||
|
|
||||||
|
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
x: 0,
|
||||||
|
y: 0.5,
|
||||||
|
z: 0
|
||||||
|
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||||
|
|
||||||
var flashlight = Entities.addEntity({
|
var flashlight = Entities.addEntity({
|
||||||
type: "Model",
|
type: "Model",
|
||||||
modelURL: modelURL,
|
modelURL: modelURL,
|
||||||
position: center,
|
position: center,
|
||||||
dimensions: {
|
dimensions: {
|
||||||
x: 0.04,
|
x: 0.08,
|
||||||
y: 0.15,
|
y: 0.30,
|
||||||
z: 0.04
|
z: 0.08
|
||||||
},
|
},
|
||||||
collisionsWillMove: true,
|
collisionsWillMove: true,
|
||||||
shapeType: 'box',
|
shapeType: 'box',
|
||||||
|
@ -38,7 +41,8 @@ var flashlight = Entities.addEntity({
|
||||||
|
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
Entities.deleteEntity(flashlight);
|
//commenting out the line below makes this persistent. to delete at cleanup, uncomment
|
||||||
|
//Entities.deleteEntity(flashlight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
//
|
//
|
||||||
// flashligh.js
|
// flashlight.js
|
||||||
// examples/entityScripts
|
//
|
||||||
|
// Script Type: Entity
|
||||||
//
|
//
|
||||||
// Created by Sam Gateau on 9/9/15.
|
// Created by Sam Gateau on 9/9/15.
|
||||||
|
// Additions by James B. Pollack @imgntn on 9/21/2015
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
// This is a toy script that can be added to the Flashlight model entity:
|
// This is a toy script that can be added to the Flashlight model entity:
|
||||||
|
@ -15,10 +17,6 @@
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
function debugPrint(message) {
|
|
||||||
//print(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
Script.include("../../libraries/utils.js");
|
Script.include("../../libraries/utils.js");
|
||||||
|
|
||||||
var _this;
|
var _this;
|
||||||
|
@ -27,145 +25,222 @@
|
||||||
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
||||||
Flashlight = function() {
|
Flashlight = function() {
|
||||||
_this = this;
|
_this = this;
|
||||||
_this._hasSpotlight = false;
|
|
||||||
_this._spotlight = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//if the trigger value goes below this while held, the flashlight will turn off. if it goes above, it will
|
||||||
GRAB_FRAME_USER_DATA_KEY = "grabFrame";
|
var DISABLE_LIGHT_THRESHOLD = 0.7;
|
||||||
|
|
||||||
// These constants define the Flashlight model Grab Frame
|
|
||||||
var MODEL_GRAB_FRAME = {
|
|
||||||
relativePosition: {x: 0, y: -0.1, z: 0},
|
|
||||||
relativeRotation: Quat.angleAxis(180, {x: 1, y: 0, z: 0})
|
|
||||||
};
|
|
||||||
|
|
||||||
// These constants define the Spotlight position and orientation relative to the model
|
// These constants define the Spotlight position and orientation relative to the model
|
||||||
var MODEL_LIGHT_POSITION = {x: 0, y: 0, z: 0};
|
var MODEL_LIGHT_POSITION = {
|
||||||
var MODEL_LIGHT_ROTATION = Quat.angleAxis (-90, {x: 1, y: 0, z: 0});
|
x: 0,
|
||||||
|
y: -0.3,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
|
var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, {
|
||||||
|
x: 1,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
|
||||||
// Evaluate the world light entity position and orientation from the model ones
|
var GLOW_LIGHT_POSITION = {
|
||||||
|
x: 0,
|
||||||
|
y: -0.1,
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate the world light entity positions and orientations from the model ones
|
||||||
function evalLightWorldTransform(modelPos, modelRot) {
|
function evalLightWorldTransform(modelPos, modelRot) {
|
||||||
return {p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
|
|
||||||
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)};
|
return {
|
||||||
|
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)),
|
||||||
|
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function glowLightWorldTransform(modelPos, modelRot) {
|
||||||
|
return {
|
||||||
|
p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, GLOW_LIGHT_POSITION)),
|
||||||
|
q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Flashlight.prototype = {
|
Flashlight.prototype = {
|
||||||
|
lightOn: false,
|
||||||
|
hand: null,
|
||||||
|
whichHand: null,
|
||||||
|
hasSpotlight: false,
|
||||||
|
spotlight: null,
|
||||||
|
setRightHand: function() {
|
||||||
|
this.hand = 'RIGHT';
|
||||||
|
},
|
||||||
|
setLeftHand: function() {
|
||||||
|
this.hand = 'LEFT';
|
||||||
|
},
|
||||||
|
startNearGrab: function() {
|
||||||
|
if (!_this.hasSpotlight) {
|
||||||
|
|
||||||
|
//this light casts the beam
|
||||||
|
this.spotlight = Entities.addEntity({
|
||||||
// update() will be called regulary, because we've hooked the update signal in our preload() function
|
|
||||||
// we will check out userData for the grabData. In the case of the hydraGrab script, it will tell us
|
|
||||||
// if we're currently being grabbed and if the person grabbing us is the current interfaces avatar.
|
|
||||||
// we will watch this for state changes and print out if we're being grabbed or released when it changes.
|
|
||||||
update: function() {
|
|
||||||
var GRAB_USER_DATA_KEY = "grabKey";
|
|
||||||
|
|
||||||
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
|
|
||||||
var entityID = _this.entityID;
|
|
||||||
|
|
||||||
// we want to assume that if there is no grab data, then we are not being grabbed
|
|
||||||
var defaultGrabData = { activated: false, avatarId: null };
|
|
||||||
|
|
||||||
// this handy function getEntityCustomData() is available in utils.js and it will return just the specific section
|
|
||||||
// of user data we asked for. If it's not available it returns our default data.
|
|
||||||
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, defaultGrabData);
|
|
||||||
|
|
||||||
|
|
||||||
// if the grabData says we're being grabbed, and the owner ID is our session, then we are being grabbed by this interface
|
|
||||||
if (grabData.activated && grabData.avatarId == MyAvatar.sessionUUID) {
|
|
||||||
|
|
||||||
// remember we're being grabbed so we can detect being released
|
|
||||||
_this.beingGrabbed = true;
|
|
||||||
|
|
||||||
var modelProperties = Entities.getEntityProperties(entityID);
|
|
||||||
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
|
||||||
|
|
||||||
// Create the spot light driven by this model if we don;t have one yet
|
|
||||||
// Or make sure to keep it's position in sync
|
|
||||||
if (!_this._hasSpotlight) {
|
|
||||||
|
|
||||||
_this._spotlight = Entities.addEntity({
|
|
||||||
type: "Light",
|
type: "Light",
|
||||||
position: lightTransform.p,
|
|
||||||
rotation: lightTransform.q,
|
|
||||||
isSpotlight: true,
|
isSpotlight: true,
|
||||||
dimensions: { x: 2, y: 2, z: 20 },
|
dimensions: {
|
||||||
color: { red: 255, green: 255, blue: 255 },
|
x: 2,
|
||||||
|
y: 2,
|
||||||
|
z: 20
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
red: 255,
|
||||||
|
green: 255,
|
||||||
|
blue: 255
|
||||||
|
},
|
||||||
intensity: 2,
|
intensity: 2,
|
||||||
exponent: 0.3,
|
exponent: 0.3,
|
||||||
cutoff: 20
|
cutoff: 20
|
||||||
});
|
});
|
||||||
_this._hasSpotlight = true;
|
|
||||||
|
|
||||||
_this._startModelPosition = modelProperties.position;
|
//this light creates the effect of a bulb at the end of the flashlight
|
||||||
_this._startModelRotation = modelProperties.rotation;
|
this.glowLight = Entities.addEntity({
|
||||||
|
type: "Light",
|
||||||
|
dimensions: {
|
||||||
|
x: 0.25,
|
||||||
|
y: 0.25,
|
||||||
|
z: 0.25
|
||||||
|
},
|
||||||
|
isSpotlight: false,
|
||||||
|
color: {
|
||||||
|
red: 255,
|
||||||
|
green: 255,
|
||||||
|
blue: 255
|
||||||
|
},
|
||||||
|
exponent: 0,
|
||||||
|
cutoff: 90, // in degrees
|
||||||
|
});
|
||||||
|
|
||||||
debugPrint("Flashlight:: creating a spotlight");
|
this.debugBox = Entities.addEntity({
|
||||||
|
type: 'Box',
|
||||||
|
color: {
|
||||||
|
red: 255,
|
||||||
|
blue: 0,
|
||||||
|
green: 0
|
||||||
|
},
|
||||||
|
dimensions: {
|
||||||
|
x: 0.25,
|
||||||
|
y: 0.25,
|
||||||
|
z: 0.25
|
||||||
|
},
|
||||||
|
ignoreForCollisions:true
|
||||||
|
})
|
||||||
|
|
||||||
|
this.hasSpotlight = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
setWhichHand: function() {
|
||||||
|
this.whichHand = this.hand;
|
||||||
|
},
|
||||||
|
continueNearGrab: function() {
|
||||||
|
if (this.whichHand === null) {
|
||||||
|
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
|
||||||
|
this.setWhichHand();
|
||||||
} else {
|
} else {
|
||||||
// Updating the spotlight
|
this.updateLightPositions();
|
||||||
Entities.editEntity(_this._spotlight, {position: lightTransform.p, rotation: lightTransform.q});
|
this.changeLightWithTriggerPressure(this.whichHand);
|
||||||
|
|
||||||
debugPrint("Flashlight:: updating the spotlight");
|
|
||||||
}
|
|
||||||
|
|
||||||
debugPrint("I'm being grabbed...");
|
|
||||||
|
|
||||||
} else if (_this.beingGrabbed) {
|
|
||||||
|
|
||||||
if (_this._hasSpotlight) {
|
|
||||||
Entities.deleteEntity(_this._spotlight);
|
|
||||||
debugPrint("Destroying flashlight spotlight...");
|
|
||||||
}
|
|
||||||
_this._hasSpotlight = false;
|
|
||||||
_this._spotlight = null;
|
|
||||||
|
|
||||||
// Reset model to initial position
|
|
||||||
Entities.editEntity(_this.entityID, {position: _this._startModelPosition, rotation: _this._startModelRotation,
|
|
||||||
velocity: {x: 0, y: 0, z: 0}, angularVelocity: {x: 0, y: 0, z: 0}});
|
|
||||||
|
|
||||||
// if we are not being grabbed, and we previously were, then we were just released, remember that
|
|
||||||
// and print out a message
|
|
||||||
_this.beingGrabbed = false;
|
|
||||||
debugPrint("I'm was released...");
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
releaseGrab: function() {
|
||||||
|
//delete the lights and reset state
|
||||||
|
if (this.hasSpotlight) {
|
||||||
|
Entities.deleteEntity(this.spotlight);
|
||||||
|
Entities.deleteEntity(this.glowLight);
|
||||||
|
this.hasSpotlight = false;
|
||||||
|
this.glowLight = null;
|
||||||
|
this.spotlight = null;
|
||||||
|
this.whichHand = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateLightPositions: function() {
|
||||||
|
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||||
|
|
||||||
|
//move the two lights along the vectors we set above
|
||||||
|
var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||||
|
var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation);
|
||||||
|
|
||||||
|
|
||||||
|
//move them with the entity model
|
||||||
|
Entities.editEntity(this.spotlight, {
|
||||||
|
position: lightTransform.p,
|
||||||
|
rotation: lightTransform.q,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
Entities.editEntity(this.glowLight, {
|
||||||
|
position: glowLightTransform.p,
|
||||||
|
rotation: glowLightTransform.q,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Entities.editEntity(this.debugBox, {
|
||||||
|
// position: lightTransform.p,
|
||||||
|
// rotation: lightTransform.q,
|
||||||
|
// })
|
||||||
|
|
||||||
|
},
|
||||||
|
changeLightWithTriggerPressure: function(flashLightHand) {
|
||||||
|
|
||||||
|
var handClickString = flashLightHand + "_HAND_CLICK";
|
||||||
|
|
||||||
|
var handClick = Controller.findAction(handClickString);
|
||||||
|
|
||||||
|
this.triggerValue = Controller.getActionValue(handClick);
|
||||||
|
|
||||||
|
if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) {
|
||||||
|
this.turnLightOff();
|
||||||
|
} else if (this.triggerValue >= DISABLE_LIGHT_THRESHOLD && this.lightOn === false) {
|
||||||
|
this.turnLightOn();
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
turnLightOff: function() {
|
||||||
|
Entities.editEntity(this.spotlight, {
|
||||||
|
intensity: 0
|
||||||
|
});
|
||||||
|
Entities.editEntity(this.glowLight, {
|
||||||
|
intensity: 0
|
||||||
|
});
|
||||||
|
this.lightOn = false
|
||||||
|
},
|
||||||
|
turnLightOn: function() {
|
||||||
|
Entities.editEntity(this.glowLight, {
|
||||||
|
intensity: 2
|
||||||
|
});
|
||||||
|
Entities.editEntity(this.spotlight, {
|
||||||
|
intensity: 2
|
||||||
|
});
|
||||||
|
this.lightOn = true
|
||||||
|
},
|
||||||
// preload() will be called when the entity has become visible (or known) to the interface
|
// preload() will be called when the entity has become visible (or known) to the interface
|
||||||
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
||||||
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
||||||
// * connecting to the update signal so we can check our grabbed state
|
|
||||||
preload: function(entityID) {
|
preload: function(entityID) {
|
||||||
_this.entityID = entityID;
|
this.entityID = entityID;
|
||||||
|
|
||||||
var modelProperties = Entities.getEntityProperties(entityID);
|
|
||||||
_this._startModelPosition = modelProperties.position;
|
|
||||||
_this._startModelRotation = modelProperties.rotation;
|
|
||||||
|
|
||||||
// Make sure the Flashlight entity has a correct grab frame setup
|
|
||||||
var userData = getEntityUserData(entityID);
|
|
||||||
debugPrint(JSON.stringify(userData));
|
|
||||||
if (!userData.grabFrame) {
|
|
||||||
setEntityCustomData(GRAB_FRAME_USER_DATA_KEY, entityID, MODEL_GRAB_FRAME);
|
|
||||||
debugPrint(JSON.stringify(MODEL_GRAB_FRAME));
|
|
||||||
debugPrint("Assigned the grab frmae for the Flashlight entity");
|
|
||||||
}
|
|
||||||
|
|
||||||
Script.update.connect(this.update);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
// unload() will be called when our entity is no longer available. It may be because we were deleted,
|
||||||
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
|
// or because we've left the domain or quit the application.
|
||||||
// to the update signal
|
|
||||||
unload: function(entityID) {
|
unload: function(entityID) {
|
||||||
|
|
||||||
if (_this._hasSpotlight) {
|
if (this.hasSpotlight) {
|
||||||
Entities.deleteEntity(_this._spotlight);
|
Entities.deleteEntity(this.spotlight);
|
||||||
|
Entities.deleteEntity(this.glowLight);
|
||||||
|
this.hasSpotlight = false;
|
||||||
|
this.glowLight = null;
|
||||||
|
this.spotlight = null;
|
||||||
|
this.whichHand = null;
|
||||||
}
|
}
|
||||||
_this._hasSpotlight = false;
|
|
||||||
_this._spotlight = null;
|
|
||||||
|
|
||||||
Script.update.disconnect(this.update);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -605,7 +605,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_overlays.init(); // do this before scripts load
|
_overlays.init(); // do this before scripts load
|
||||||
|
|
||||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||||
connect(_runningScriptsWidget, &RunningScriptsWidget::stopScriptName, this, &Application::stopScript);
|
|
||||||
|
|
||||||
connect(this, SIGNAL(aboutToQuit()), this, SLOT(saveScripts()));
|
connect(this, SIGNAL(aboutToQuit()), this, SLOT(saveScripts()));
|
||||||
connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
|
connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
|
||||||
|
@ -4332,17 +4331,18 @@ void Application::stopAllScripts(bool restart) {
|
||||||
_myAvatar->clearScriptableSettings();
|
_myAvatar->clearScriptableSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::stopScript(const QString &scriptName, bool restart) {
|
bool Application::stopScript(const QString& scriptHash, bool restart) {
|
||||||
const QString& scriptURLString = QUrl(scriptName).toString();
|
bool stoppedScript = false;
|
||||||
if (_scriptEnginesHash.contains(scriptURLString)) {
|
if (_scriptEnginesHash.contains(scriptHash)) {
|
||||||
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURLString];
|
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash];
|
||||||
if (restart) {
|
if (restart) {
|
||||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||||
scriptCache->deleteScript(scriptName);
|
scriptCache->deleteScript(QUrl(scriptHash));
|
||||||
connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
|
connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
|
||||||
}
|
}
|
||||||
scriptEngine->stop();
|
scriptEngine->stop();
|
||||||
qCDebug(interfaceapp) << "stopping script..." << scriptName;
|
stoppedScript = true;
|
||||||
|
qCDebug(interfaceapp) << "stopping script..." << scriptHash;
|
||||||
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
|
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
|
||||||
// whenever a script stops in case it happened to have been setting joint rotations.
|
// whenever a script stops in case it happened to have been setting joint rotations.
|
||||||
// TODO: expose animation priorities and provide a layered animation control system.
|
// TODO: expose animation priorities and provide a layered animation control system.
|
||||||
|
@ -4351,6 +4351,7 @@ void Application::stopScript(const QString &scriptName, bool restart) {
|
||||||
if (_scriptEnginesHash.empty()) {
|
if (_scriptEnginesHash.empty()) {
|
||||||
_myAvatar->clearScriptableSettings();
|
_myAvatar->clearScriptableSettings();
|
||||||
}
|
}
|
||||||
|
return stoppedScript;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::reloadAllScripts() {
|
void Application::reloadAllScripts() {
|
||||||
|
|
|
@ -292,7 +292,7 @@ public:
|
||||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||||
|
|
||||||
QStringList getRunningScripts() { return _scriptEnginesHash.keys(); }
|
QStringList getRunningScripts() { return _scriptEnginesHash.keys(); }
|
||||||
ScriptEngine* getScriptEngine(QString scriptHash) { return _scriptEnginesHash.contains(scriptHash) ? _scriptEnginesHash[scriptHash] : NULL; }
|
ScriptEngine* getScriptEngine(const QString& scriptHash) { return _scriptEnginesHash.value(scriptHash, NULL); }
|
||||||
|
|
||||||
bool isLookingAtMyAvatar(AvatarSharedPointer avatar);
|
bool isLookingAtMyAvatar(AvatarSharedPointer avatar);
|
||||||
|
|
||||||
|
@ -392,7 +392,7 @@ public slots:
|
||||||
void reloadScript(const QString& scriptName, bool isUserLoaded = true);
|
void reloadScript(const QString& scriptName, bool isUserLoaded = true);
|
||||||
void scriptFinished(const QString& scriptName);
|
void scriptFinished(const QString& scriptName);
|
||||||
void stopAllScripts(bool restart = false);
|
void stopAllScripts(bool restart = false);
|
||||||
void stopScript(const QString& scriptName, bool restart = false);
|
bool stopScript(const QString& scriptHash, bool restart = false);
|
||||||
void reloadAllScripts();
|
void reloadAllScripts();
|
||||||
void reloadOneScript(const QString& scriptName);
|
void reloadOneScript(const QString& scriptName);
|
||||||
void loadDefaultScripts();
|
void loadDefaultScripts();
|
||||||
|
|
|
@ -110,6 +110,8 @@ public:
|
||||||
Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); }
|
Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); }
|
||||||
Q_INVOKABLE float getHeadFinalPitch() const { return getHead()->getFinalPitch(); }
|
Q_INVOKABLE float getHeadFinalPitch() const { return getHead()->getFinalPitch(); }
|
||||||
Q_INVOKABLE float getHeadDeltaPitch() const { return getHead()->getDeltaPitch(); }
|
Q_INVOKABLE float getHeadDeltaPitch() const { return getHead()->getDeltaPitch(); }
|
||||||
|
Q_INVOKABLE int getFaceBlendCoefNum() const { return getHead()->getFaceModel().getBlendshapeCoefficientsNum(); }
|
||||||
|
Q_INVOKABLE float getFaceBlendCoef(int index) const { return getHead()->getFaceModel().getBlendshapeCoefficient(index); }
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); }
|
Q_INVOKABLE glm::vec3 getEyePosition() const { return getHead()->getEyePosition(); }
|
||||||
|
|
||||||
|
|
|
@ -57,16 +57,15 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) :
|
||||||
connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &RunningScriptsWidget::updateFileFilter);
|
connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &RunningScriptsWidget::updateFileFilter);
|
||||||
connect(ui->scriptTreeView, &QTreeView::doubleClicked, this, &RunningScriptsWidget::loadScriptFromList);
|
connect(ui->scriptTreeView, &QTreeView::doubleClicked, this, &RunningScriptsWidget::loadScriptFromList);
|
||||||
|
|
||||||
connect(ui->reloadAllButton, &QPushButton::clicked,
|
connect(ui->reloadAllButton, &QPushButton::clicked, Application::getInstance(), &Application::reloadAllScripts);
|
||||||
Application::getInstance(), &Application::reloadAllScripts);
|
connect(ui->stopAllButton, &QPushButton::clicked, this, &RunningScriptsWidget::allScriptsStopped);
|
||||||
connect(ui->stopAllButton, &QPushButton::clicked,
|
connect(ui->loadScriptFromDiskButton, &QPushButton::clicked, Application::getInstance(), &Application::loadDialog);
|
||||||
this, &RunningScriptsWidget::allScriptsStopped);
|
connect(ui->loadScriptFromURLButton, &QPushButton::clicked, Application::getInstance(), &Application::loadScriptURLDialog);
|
||||||
connect(ui->loadScriptFromDiskButton, &QPushButton::clicked,
|
connect(&_reloadSignalMapper, static_cast<void(QSignalMapper::*)(const QString&)>(&QSignalMapper::mapped),
|
||||||
Application::getInstance(), &Application::loadDialog);
|
Application::getInstance(), &Application::reloadOneScript);
|
||||||
connect(ui->loadScriptFromURLButton, &QPushButton::clicked,
|
|
||||||
Application::getInstance(), &Application::loadScriptURLDialog);
|
connect(&_stopSignalMapper, static_cast<void(QSignalMapper::*)(const QString&)>(&QSignalMapper::mapped),
|
||||||
connect(&_reloadSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(reloadOneScript(const QString&)));
|
[](const QString& script) { Application::getInstance()->stopScript(script); });
|
||||||
connect(&_stopSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(stopScript(const QString&)));
|
|
||||||
|
|
||||||
UIUtil::scaleWidgetFontSizes(this);
|
UIUtil::scaleWidgetFontSizes(this);
|
||||||
}
|
}
|
||||||
|
@ -217,9 +216,6 @@ void RunningScriptsWidget::keyPressEvent(QKeyEvent *keyEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunningScriptsWidget::scriptStopped(const QString& scriptName) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunningScriptsWidget::allScriptsStopped() {
|
void RunningScriptsWidget::allScriptsStopped() {
|
||||||
Application::getInstance()->stopAllScripts();
|
Application::getInstance()->stopAllScripts();
|
||||||
}
|
}
|
||||||
|
@ -227,15 +223,16 @@ void RunningScriptsWidget::allScriptsStopped() {
|
||||||
QVariantList RunningScriptsWidget::getRunning() {
|
QVariantList RunningScriptsWidget::getRunning() {
|
||||||
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
|
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
|
||||||
QVariantList result;
|
QVariantList result;
|
||||||
QStringList runningScripts = Application::getInstance()->getRunningScripts();
|
foreach(const QString& runningScript, Application::getInstance()->getRunningScripts()) {
|
||||||
for (int i = 0; i < runningScripts.size(); i++) {
|
QUrl runningScriptURL = QUrl(runningScript);
|
||||||
QUrl runningScriptURL = QUrl(runningScripts.at(i));
|
|
||||||
if (runningScriptURL.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
if (runningScriptURL.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
||||||
runningScriptURL = QUrl::fromLocalFile(runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
|
runningScriptURL = QUrl::fromLocalFile(runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
|
||||||
}
|
}
|
||||||
QVariantMap resultNode;
|
QVariantMap resultNode;
|
||||||
resultNode.insert("name", runningScriptURL.fileName());
|
resultNode.insert("name", runningScriptURL.fileName());
|
||||||
resultNode.insert("url", runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
|
resultNode.insert("url", runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
|
||||||
|
// The path contains the exact path/URL of the script, which also is used in the stopScript function.
|
||||||
|
resultNode.insert("path", runningScript);
|
||||||
resultNode.insert("local", runningScriptURL.isLocalFile());
|
resultNode.insert("local", runningScriptURL.isLocalFile());
|
||||||
result.append(resultNode);
|
result.append(resultNode);
|
||||||
}
|
}
|
||||||
|
@ -294,3 +291,16 @@ QVariantList RunningScriptsWidget::getLocal() {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RunningScriptsWidget::stopScriptByName(const QString& name) {
|
||||||
|
foreach (const QString& runningScript, Application::getInstance()->getRunningScripts()) {
|
||||||
|
if (QUrl(runningScript).fileName().toLower() == name.trimmed().toLower()) {
|
||||||
|
return Application::getInstance()->stopScript(runningScript, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RunningScriptsWidget::stopScript(const QString& name, bool restart) {
|
||||||
|
return Application::getInstance()->stopScript(name, restart);
|
||||||
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public:
|
||||||
const ScriptsModel* getScriptsModel() { return &_scriptsModel; }
|
const ScriptsModel* getScriptsModel() { return &_scriptsModel; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void stopScriptName(const QString& name, bool restart = false);
|
void scriptStopped(const QString& scriptName);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool eventFilter(QObject* sender, QEvent* event);
|
virtual bool eventFilter(QObject* sender, QEvent* event);
|
||||||
|
@ -45,10 +45,11 @@ protected:
|
||||||
virtual void showEvent(QShowEvent* event);
|
virtual void showEvent(QShowEvent* event);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void scriptStopped(const QString& scriptName);
|
|
||||||
QVariantList getRunning();
|
QVariantList getRunning();
|
||||||
QVariantList getPublic();
|
QVariantList getPublic();
|
||||||
QVariantList getLocal();
|
QVariantList getLocal();
|
||||||
|
bool stopScript(const QString& name, bool restart = false);
|
||||||
|
bool stopScriptByName(const QString& name);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void allScriptsStopped();
|
void allScriptsStopped();
|
||||||
|
@ -63,9 +64,6 @@ private:
|
||||||
QSignalMapper _stopSignalMapper;
|
QSignalMapper _stopSignalMapper;
|
||||||
ScriptsModelFilter _scriptsModelFilter;
|
ScriptsModelFilter _scriptsModelFilter;
|
||||||
ScriptsModel _scriptsModel;
|
ScriptsModel _scriptsModel;
|
||||||
ScriptsTableWidget* _recentlyLoadedScriptsTable;
|
|
||||||
QStringList _recentlyLoadedScripts;
|
|
||||||
QString _lastStoppedScript;
|
|
||||||
|
|
||||||
QVariantList getPublicChildNodes(TreeNodeFolder* parent);
|
QVariantList getPublicChildNodes(TreeNodeFolder* parent);
|
||||||
};
|
};
|
||||||
|
|
|
@ -82,22 +82,8 @@ static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int inde
|
||||||
return rootIndex;
|
return rootIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IKTarget {
|
void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets) {
|
||||||
AnimPose pose;
|
// build a list of valid targets from _targetVarVec and animVars
|
||||||
int index;
|
|
||||||
int rootIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
//virtual
|
|
||||||
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) {
|
|
||||||
|
|
||||||
// NOTE: we assume that _relativePoses are up to date (e.g. loadPoses() was just called)
|
|
||||||
if (_relativePoses.empty()) {
|
|
||||||
return _relativePoses;
|
|
||||||
}
|
|
||||||
|
|
||||||
// build a list of targets from _targetVarVec
|
|
||||||
std::vector<IKTarget> targets;
|
|
||||||
bool removeUnfoundJoints = false;
|
bool removeUnfoundJoints = false;
|
||||||
for (auto& targetVar : _targetVarVec) {
|
for (auto& targetVar : _targetVarVec) {
|
||||||
if (targetVar.jointIndex == -1) {
|
if (targetVar.jointIndex == -1) {
|
||||||
|
@ -113,7 +99,6 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: get this done without a double-lookup of each var in animVars
|
// TODO: get this done without a double-lookup of each var in animVars
|
||||||
if (animVars.hasKey(targetVar.positionVar) || animVars.hasKey(targetVar.rotationVar)) {
|
|
||||||
IKTarget target;
|
IKTarget target;
|
||||||
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses);
|
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses);
|
||||||
target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans);
|
target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans);
|
||||||
|
@ -123,7 +108,6 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
|
||||||
targets.push_back(target);
|
targets.push_back(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (removeUnfoundJoints) {
|
if (removeUnfoundJoints) {
|
||||||
int numVars = _targetVarVec.size();
|
int numVars = _targetVarVec.size();
|
||||||
|
@ -141,27 +125,18 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targets.empty()) {
|
|
||||||
// no IK targets but still need to enforce constraints
|
|
||||||
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin();
|
|
||||||
while (constraintItr != _constraints.end()) {
|
|
||||||
int index = constraintItr->first;
|
|
||||||
glm::quat rotation = _relativePoses[index].rot;
|
|
||||||
constraintItr->second->apply(rotation);
|
|
||||||
_relativePoses[index].rot = rotation;
|
|
||||||
++constraintItr;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
void AnimInverseKinematics::solveWithCyclicCoordinateDescent(std::vector<IKTarget>& targets) {
|
||||||
|
// compute absolute poses that correspond to relative target poses
|
||||||
|
AnimPoseVec absolutePoses;
|
||||||
|
computeAbsolutePoses(absolutePoses);
|
||||||
|
|
||||||
// clear the accumulators before we start the IK solver
|
// clear the accumulators before we start the IK solver
|
||||||
for (auto& accumulatorPair: _accumulators) {
|
for (auto& accumulatorPair: _accumulators) {
|
||||||
accumulatorPair.second.clear();
|
accumulatorPair.second.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute absolute poses that correspond to relative target poses
|
|
||||||
AnimPoseVec absolutePoses;
|
|
||||||
computeAbsolutePoses(absolutePoses);
|
|
||||||
|
|
||||||
float largestError = 0.0f;
|
float largestError = 0.0f;
|
||||||
const float ACCEPTABLE_RELATIVE_ERROR = 1.0e-3f;
|
const float ACCEPTABLE_RELATIVE_ERROR = 1.0e-3f;
|
||||||
int numLoops = 0;
|
int numLoops = 0;
|
||||||
|
@ -174,13 +149,6 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
|
||||||
for (auto& target: targets) {
|
for (auto& target: targets) {
|
||||||
int tipIndex = target.index;
|
int tipIndex = target.index;
|
||||||
AnimPose targetPose = target.pose;
|
AnimPose targetPose = target.pose;
|
||||||
int rootIndex = target.rootIndex;
|
|
||||||
if (rootIndex != -1) {
|
|
||||||
// transform targetPose into skeleton's absolute frame
|
|
||||||
AnimPose& rootPose = _relativePoses[rootIndex];
|
|
||||||
targetPose.trans = rootPose.trans + rootPose.rot * targetPose.trans;
|
|
||||||
targetPose.rot = rootPose.rot * targetPose.rot;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 tip = absolutePoses[tipIndex].trans;
|
glm::vec3 tip = absolutePoses[tipIndex].trans;
|
||||||
float error = glm::length(targetPose.trans - tip);
|
float error = glm::length(targetPose.trans - tip);
|
||||||
|
@ -291,6 +259,28 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//virtual
|
||||||
|
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) {
|
||||||
|
if (!_relativePoses.empty()) {
|
||||||
|
// build a list of targets from _targetVarVec
|
||||||
|
std::vector<IKTarget> targets;
|
||||||
|
computeTargets(animVars, targets);
|
||||||
|
|
||||||
|
if (targets.empty()) {
|
||||||
|
// no IK targets but still need to enforce constraints
|
||||||
|
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.begin();
|
||||||
|
while (constraintItr != _constraints.end()) {
|
||||||
|
int index = constraintItr->first;
|
||||||
|
glm::quat rotation = _relativePoses[index].rot;
|
||||||
|
constraintItr->second->apply(rotation);
|
||||||
|
_relativePoses[index].rot = rotation;
|
||||||
|
++constraintItr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
solveWithCyclicCoordinateDescent(targets);
|
||||||
|
}
|
||||||
|
}
|
||||||
return _relativePoses;
|
return _relativePoses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,14 @@ public:
|
||||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
struct IKTarget {
|
||||||
|
AnimPose pose;
|
||||||
|
int index;
|
||||||
|
int rootIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
void computeTargets(const AnimVariantMap& animVars, std::vector<IKTarget>& targets);
|
||||||
|
void solveWithCyclicCoordinateDescent(std::vector<IKTarget>& targets);
|
||||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton);
|
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton);
|
||||||
|
|
||||||
// for AnimDebugDraw rendering
|
// for AnimDebugDraw rendering
|
||||||
|
@ -64,7 +72,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
std::map<int, RotationConstraint*> _constraints;
|
std::map<int, RotationConstraint*> _constraints;
|
||||||
std::map<int, RotationAccumulator> _accumulators;
|
std::map<int, RotationAccumulator> _accumulators; // class-member to exploit temporal coherency
|
||||||
std::vector<IKTargetVar> _targetVarVec;
|
std::vector<IKTargetVar> _targetVarVec;
|
||||||
AnimPoseVec _defaultRelativePoses; // poses of the relaxed state
|
AnimPoseVec _defaultRelativePoses; // poses of the relaxed state
|
||||||
AnimPoseVec _relativePoses; // current relative poses
|
AnimPoseVec _relativePoses; // current relative poses
|
||||||
|
|
54
libraries/animation/src/AnimNode.cpp
Normal file
54
libraries/animation/src/AnimNode.cpp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// AnimNode.cpp
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 9/2/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 "AnimNode.h"
|
||||||
|
|
||||||
|
void AnimNode::removeChild(AnimNode::Pointer child) {
|
||||||
|
auto iter = std::find(_children.begin(), _children.end(), child);
|
||||||
|
if (iter != _children.end()) {
|
||||||
|
_children.erase(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimNode::Pointer AnimNode::getChild(int i) const {
|
||||||
|
assert(i >= 0 && i < (int)_children.size());
|
||||||
|
return _children[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimNode::setSkeleton(const AnimSkeleton::Pointer skeleton) {
|
||||||
|
setSkeletonInternal(skeleton);
|
||||||
|
for (auto&& child : _children) {
|
||||||
|
child->setSkeleton(skeleton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPose AnimNode::getRootPose(int jointIndex) const {
|
||||||
|
AnimPose pose = AnimPose::identity;
|
||||||
|
if (_skeleton && jointIndex != -1) {
|
||||||
|
const AnimPoseVec& poses = getPosesInternal();
|
||||||
|
int numJoints = (int)(poses.size());
|
||||||
|
if (jointIndex < numJoints) {
|
||||||
|
int parentIndex = _skeleton->getParentIndex(jointIndex);
|
||||||
|
while (parentIndex != -1 && parentIndex < numJoints) {
|
||||||
|
jointIndex = parentIndex;
|
||||||
|
parentIndex = _skeleton->getParentIndex(jointIndex);
|
||||||
|
}
|
||||||
|
pose = poses[jointIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pose;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimNode::setCurrentFrame(float frame) {
|
||||||
|
setCurrentFrameInternal(frame);
|
||||||
|
for (auto&& child : _children) {
|
||||||
|
child->setCurrentFrameInternal(frame);
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,25 +60,13 @@ public:
|
||||||
|
|
||||||
// hierarchy accessors
|
// hierarchy accessors
|
||||||
void addChild(Pointer child) { _children.push_back(child); }
|
void addChild(Pointer child) { _children.push_back(child); }
|
||||||
void removeChild(Pointer child) {
|
void removeChild(Pointer child);
|
||||||
auto iter = std::find(_children.begin(), _children.end(), child);
|
|
||||||
if (iter != _children.end()) {
|
Pointer getChild(int i) const;
|
||||||
_children.erase(iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Pointer getChild(int i) const {
|
|
||||||
assert(i >= 0 && i < (int)_children.size());
|
|
||||||
return _children[i];
|
|
||||||
}
|
|
||||||
int getChildCount() const { return (int)_children.size(); }
|
int getChildCount() const { return (int)_children.size(); }
|
||||||
|
|
||||||
// pair this AnimNode graph with a skeleton.
|
// pair this AnimNode graph with a skeleton.
|
||||||
void setSkeleton(const AnimSkeleton::Pointer skeleton) {
|
void setSkeleton(const AnimSkeleton::Pointer skeleton);
|
||||||
setSkeletonInternal(skeleton);
|
|
||||||
for (auto&& child : _children) {
|
|
||||||
child->setSkeleton(skeleton);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; }
|
AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; }
|
||||||
|
|
||||||
|
@ -87,19 +75,14 @@ public:
|
||||||
return evaluate(animVars, dt, triggersOut);
|
return evaluate(animVars, dt, triggersOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AnimPose getRootPose(int jointIndex) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void setCurrentFrame(float frame) {
|
void setCurrentFrame(float frame);
|
||||||
setCurrentFrameInternal(frame);
|
|
||||||
for (auto&& child : _children) {
|
|
||||||
child->setCurrentFrameInternal(frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void setCurrentFrameInternal(float frame) {}
|
virtual void setCurrentFrameInternal(float frame) {}
|
||||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { _skeleton = skeleton; }
|
||||||
_skeleton = skeleton;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for AnimDebugDraw rendering
|
// for AnimDebugDraw rendering
|
||||||
virtual const AnimPoseVec& getPosesInternal() const = 0;
|
virtual const AnimPoseVec& getPosesInternal() const = 0;
|
||||||
|
|
|
@ -986,16 +986,20 @@ void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, floa
|
||||||
|
|
||||||
void Rig::updateNeckJoint(int index, const HeadParameters& params) {
|
void Rig::updateNeckJoint(int index, const HeadParameters& params) {
|
||||||
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
|
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
|
||||||
if (_enableAnimGraph && _animSkeleton) {
|
if (_enableAnimGraph && _animSkeleton && _animNode) {
|
||||||
// the params.localHeadOrientation is composed incorrectly, so re-compose it correctly from pitch, yaw and roll.
|
// the params.localHeadOrientation is composed incorrectly, so re-compose it correctly from pitch, yaw and roll.
|
||||||
glm::quat realLocalHeadOrientation = (glm::angleAxis(glm::radians(-params.localHeadRoll), Z_AXIS) *
|
glm::quat realLocalHeadOrientation = (glm::angleAxis(glm::radians(-params.localHeadRoll), Z_AXIS) *
|
||||||
glm::angleAxis(glm::radians(params.localHeadYaw), Y_AXIS) *
|
glm::angleAxis(glm::radians(params.localHeadYaw), Y_AXIS) *
|
||||||
glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS));
|
glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS));
|
||||||
_animVars.set("headRotation", realLocalHeadOrientation);
|
_animVars.set("headRotation", realLocalHeadOrientation);
|
||||||
|
|
||||||
// There's a theory that when not in hmd, we should _animVars.unset("headPosition").
|
if (params.isInHMD) {
|
||||||
// However, until that works well, let's always request head be positioned where requested by hmd, camera, or default.
|
int headIndex = _animSkeleton->nameToJointIndex("Head");
|
||||||
_animVars.set("headPosition", params.localHeadPosition);
|
AnimPose rootPose = _animNode->getRootPose(headIndex);
|
||||||
|
_animVars.set("headPosition", rootPose.trans + params.localHeadPosition); // rootPose.rot is handled in params?d
|
||||||
|
} else {
|
||||||
|
_animVars.unset("headPosition");
|
||||||
|
}
|
||||||
} else if (!_enableAnimGraph) {
|
} else if (!_enableAnimGraph) {
|
||||||
|
|
||||||
auto& state = _jointStates[index];
|
auto& state = _jointStates[index];
|
||||||
|
@ -1044,20 +1048,22 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
||||||
|
|
||||||
void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
|
void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
|
||||||
|
|
||||||
if (_enableAnimGraph && _animSkeleton) {
|
if (_enableAnimGraph && _animSkeleton && _animNode) {
|
||||||
|
|
||||||
// TODO: figure out how to obtain the yFlip from where it is actually stored
|
// TODO: figure out how to obtain the yFlip from where it is actually stored
|
||||||
glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
int leftHandIndex = _animSkeleton->nameToJointIndex("LeftHand");
|
||||||
|
AnimPose rootPose = _animNode->getRootPose(leftHandIndex);
|
||||||
if (params.isLeftEnabled) {
|
if (params.isLeftEnabled) {
|
||||||
_animVars.set("leftHandPosition", yFlipHACK * params.leftPosition);
|
_animVars.set("leftHandPosition", rootPose.trans + rootPose.rot * yFlipHACK * params.leftPosition);
|
||||||
_animVars.set("leftHandRotation", yFlipHACK * params.leftOrientation);
|
_animVars.set("leftHandRotation", rootPose.rot * yFlipHACK * params.leftOrientation);
|
||||||
} else {
|
} else {
|
||||||
_animVars.unset("leftHandPosition");
|
_animVars.unset("leftHandPosition");
|
||||||
_animVars.unset("leftHandRotation");
|
_animVars.unset("leftHandRotation");
|
||||||
}
|
}
|
||||||
if (params.isRightEnabled) {
|
if (params.isRightEnabled) {
|
||||||
_animVars.set("rightHandPosition", yFlipHACK * params.rightPosition);
|
_animVars.set("rightHandPosition", rootPose.trans + rootPose.rot * yFlipHACK * params.rightPosition);
|
||||||
_animVars.set("rightHandRotation", yFlipHACK * params.rightOrientation);
|
_animVars.set("rightHandRotation", rootPose.rot * yFlipHACK * params.rightOrientation);
|
||||||
} else {
|
} else {
|
||||||
_animVars.unset("rightHandPosition");
|
_animVars.unset("rightHandPosition");
|
||||||
_animVars.unset("rightHandRotation");
|
_animVars.unset("rightHandRotation");
|
||||||
|
|
|
@ -15,18 +15,6 @@
|
||||||
using namespace model;
|
using namespace model;
|
||||||
using namespace gpu;
|
using namespace gpu;
|
||||||
|
|
||||||
float componentSRGBToLinear(float cs) {
|
|
||||||
if (cs > 0.04045) {
|
|
||||||
return pow(((cs + 0.055)/1.055), 2.4);
|
|
||||||
} else {
|
|
||||||
return cs / 12.92;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 convertSRGBToLinear(const glm::vec3& srgb) {
|
|
||||||
return glm::vec3(componentSRGBToLinear(srgb.x), componentSRGBToLinear(srgb.y), componentSRGBToLinear(srgb.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
Material::Material() :
|
Material::Material() :
|
||||||
_key(0),
|
_key(0),
|
||||||
_schemaBuffer(),
|
_schemaBuffer(),
|
||||||
|
|
|
@ -20,8 +20,6 @@
|
||||||
|
|
||||||
namespace model {
|
namespace model {
|
||||||
|
|
||||||
static glm::vec3 convertSRGBToLinear(const glm::vec3& srgb);
|
|
||||||
|
|
||||||
class TextureMap;
|
class TextureMap;
|
||||||
typedef std::shared_ptr< TextureMap > TextureMapPointer;
|
typedef std::shared_ptr< TextureMap > TextureMapPointer;
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,11 @@ public:
|
||||||
const std::unordered_set<int>& getCauterizeBoneSet() const { return _cauterizeBoneSet; }
|
const std::unordered_set<int>& getCauterizeBoneSet() const { return _cauterizeBoneSet; }
|
||||||
void setCauterizeBoneSet(const std::unordered_set<int>& boneSet) { _cauterizeBoneSet = boneSet; }
|
void setCauterizeBoneSet(const std::unordered_set<int>& boneSet) { _cauterizeBoneSet = boneSet; }
|
||||||
|
|
||||||
|
int getBlendshapeCoefficientsNum() const { return _blendshapeCoefficients.size(); }
|
||||||
|
float getBlendshapeCoefficient(unsigned int index) const {
|
||||||
|
return index >= _blendshapeCoefficients.size() ? 0.0f : _blendshapeCoefficients.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
|
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
|
||||||
|
|
Loading…
Reference in a new issue