mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
This commit is contained in:
commit
656849199c
18 changed files with 551 additions and 446 deletions
|
@ -141,18 +141,6 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
return packetsSent; // without sending...
|
||||
}
|
||||
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
|
||||
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
|
||||
// If we've got a stats message ready to send, then see if we can piggyback them together
|
||||
if (nodeData->stats.isReadyToSend() && !nodeData->isShuttingDown()) {
|
||||
// Send the stats message to the client
|
||||
|
@ -174,7 +162,17 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
_totalBytes += nodeData->getPacketLength();
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
qDebug() << "Adding stats to packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
|
||||
" timestamp: " << timestamp <<
|
||||
" statsMessageLength: " << statsMessageLength <<
|
||||
" original size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
|
||||
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
|
||||
|
@ -196,7 +194,17 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
_totalBytes += statsMessageLength;
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
qDebug() << "Sending separate stats packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
|
||||
" timestamp: " << timestamp <<
|
||||
" size: " << statsMessageLength << " [" << _totalBytes <<
|
||||
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
|
||||
}
|
||||
|
@ -215,7 +223,17 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
_totalBytes += nodeData->getPacketLength();
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
|
||||
" timestamp: " << timestamp <<
|
||||
" size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
|
||||
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
|
||||
}
|
||||
|
@ -234,7 +252,17 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
|
|||
_totalBytes += nodeData->getPacketLength();
|
||||
_totalPackets++;
|
||||
if (debug) {
|
||||
const unsigned char* messageData = nodeData->getPacket();
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(messageData));
|
||||
const unsigned char* dataAt = messageData + numBytesPacketHeader;
|
||||
dataAt += sizeof(OCTREE_PACKET_FLAGS);
|
||||
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
|
||||
OCTREE_PACKET_SENT_TIME timestamp = (*(OCTREE_PACKET_SENT_TIME*)dataAt);
|
||||
dataAt += sizeof(OCTREE_PACKET_SENT_TIME);
|
||||
|
||||
qDebug() << "Sending packet at " << now << " [" << _totalPackets <<"]: sequence: " << sequence <<
|
||||
" timestamp: " << timestamp <<
|
||||
" size: " << nodeData->getPacketLength() << " [" << _totalBytes <<
|
||||
"] wasted bytes:" << thisWastedBytes << " [" << _totalWastedBytes << "]";
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
Script.include("lookWithTouch.js");
|
||||
Script.include("editVoxels.js");
|
||||
Script.include("editModels.js");
|
||||
Script.include("selectAudioDevice.js");
|
||||
Script.include("hydraMove.js");
|
||||
Script.include("inspect.js");
|
|
@ -766,18 +766,30 @@ function mouseReleaseEvent(event) {
|
|||
glowedModelID.isKnownID = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already
|
||||
// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that
|
||||
// added it.
|
||||
var modelMenuAddedDelete = false;
|
||||
function setupModelMenus() {
|
||||
print("setupModelMenus()");
|
||||
// add our menuitems
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete Model", shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" });
|
||||
if (!Menu.menuItemExists("Edit","Delete")) {
|
||||
print("no delete... adding ours");
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete",
|
||||
shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" });
|
||||
modelMenuAddedDelete = true;
|
||||
} else {
|
||||
print("delete exists... don't add ours");
|
||||
}
|
||||
}
|
||||
|
||||
function cleanupModelMenus() {
|
||||
// delete our menuitems
|
||||
Menu.removeSeparator("Edit", "Models");
|
||||
Menu.removeMenuItem("Edit", "Delete Model");
|
||||
if (modelMenuAddedDelete) {
|
||||
// delete our menuitems
|
||||
Menu.removeSeparator("Edit", "Models");
|
||||
Menu.removeMenuItem("Edit", "Delete");
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
|
@ -797,7 +809,7 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
|||
setupModelMenus();
|
||||
Menu.menuItemEvent.connect(function(menuItem){
|
||||
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
||||
if (menuItem == "Delete Model") {
|
||||
if (menuItem == "Delete") {
|
||||
if (leftController.grabbing) {
|
||||
print(" Delete Model.... leftController.modelID="+ leftController.modelID);
|
||||
Models.deleteModel(leftController.modelID);
|
||||
|
|
|
@ -1117,7 +1117,12 @@ function keyReleaseEvent(event) {
|
|||
trackKeyReleaseEvent(event); // used by preview support
|
||||
}
|
||||
|
||||
function setupMenus() {
|
||||
|
||||
// In order for editVoxels and editModels to play nice together, they each check to see if a "delete" menu item already
|
||||
// exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that
|
||||
// added it.
|
||||
var voxelMenuAddedDelete = false;
|
||||
function setupVoxelMenus() {
|
||||
// hook up menus
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
|
||||
|
@ -1127,7 +1132,13 @@ function setupMenus() {
|
|||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Copy", shortcutKey: "CTRL+C", afterItem: "Cut" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste", shortcutKey: "CTRL+V", afterItem: "Copy" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Nudge", shortcutKey: "CTRL+N", afterItem: "Paste" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" });
|
||||
|
||||
|
||||
if (!Menu.menuItemExists("Edit","Delete")) {
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete",
|
||||
shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" });
|
||||
voxelMenuAddedDelete = true;
|
||||
}
|
||||
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Voxels", isSeparator: true, beforeItem: "Settings" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Voxels", shortcutKey: "CTRL+E", afterItem: "Voxels" });
|
||||
|
@ -1141,7 +1152,9 @@ function cleanupMenus() {
|
|||
Menu.removeMenuItem("Edit", "Copy");
|
||||
Menu.removeMenuItem("Edit", "Paste");
|
||||
Menu.removeMenuItem("Edit", "Nudge");
|
||||
Menu.removeMenuItem("Edit", "Delete");
|
||||
if (voxelMenuAddedDelete) {
|
||||
Menu.removeMenuItem("Edit", "Delete");
|
||||
}
|
||||
Menu.removeSeparator("File", "Voxels");
|
||||
Menu.removeMenuItem("File", "Export Voxels");
|
||||
Menu.removeMenuItem("File", "Import Voxels");
|
||||
|
@ -1482,4 +1495,4 @@ Script.scriptEnding.connect(scriptEnding);
|
|||
|
||||
Script.update.connect(update);
|
||||
|
||||
setupMenus();
|
||||
setupVoxelMenus();
|
||||
|
|
|
@ -13,68 +13,88 @@
|
|||
|
||||
|
||||
function setupMenus() {
|
||||
Menu.addMenu("Foo");
|
||||
Menu.addMenuItem("Foo","Foo item 1", "SHIFT+CTRL+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 2", "SHIFT+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 3", "META+F" );
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 4",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
});
|
||||
if (!Menu.menuExists("Foo")) {
|
||||
Window.alert("Adding Menu Foo!");
|
||||
Menu.addMenu("Foo");
|
||||
Menu.addMenuItem("Foo","Foo item 1", "SHIFT+CTRL+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 2", "SHIFT+F" );
|
||||
Menu.addMenuItem("Foo","Foo item 3", "META+F" );
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 5",
|
||||
shortcutKey: "ALT+F",
|
||||
isCheckable: true
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 4",
|
||||
isCheckable: true,
|
||||
isChecked: true
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Foo item 5",
|
||||
shortcutKey: "ALT+F",
|
||||
isCheckable: true
|
||||
});
|
||||
|
||||
|
||||
Menu.addSeparator("Foo","Removable Tools");
|
||||
Menu.addMenuItem("Foo","Remove Foo item 4");
|
||||
Menu.addMenuItem("Foo","Remove Foo");
|
||||
Menu.addMenuItem("Foo","Remove Bar-Spam");
|
||||
Menu.addMenu("Bar");
|
||||
Menu.addSeparator("Foo","Removable Tools");
|
||||
Menu.addMenuItem("Foo","Remove Foo item 4");
|
||||
Menu.addMenuItem("Foo","Remove Foo");
|
||||
Menu.addMenuItem("Foo","Remove Bar-Spam");
|
||||
|
||||
Menu.addMenuItem("Bar","Bar item 1", "b");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar",
|
||||
menuItemName: "Bar item 2",
|
||||
shortcutKeyEvent: { text: "B", isControl: true }
|
||||
});
|
||||
Menu.addMenuItem("Foo","Remove Spam item 2");
|
||||
|
||||
Menu.addMenu("Bar > Spam");
|
||||
Menu.addMenuItem("Bar > Spam","Spam item 1");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar > Spam",
|
||||
menuItemName: "Spam item 2",
|
||||
isCheckable: true,
|
||||
isChecked: false
|
||||
});
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Remove Spam item 2"
|
||||
});
|
||||
} else {
|
||||
Window.alert("Menu Foo already exists!");
|
||||
}
|
||||
|
||||
if (!Menu.menuExists("Bar")) {
|
||||
Window.alert("Adding Menu Bar!");
|
||||
Menu.addMenu("Bar");
|
||||
Menu.addMenuItem("Bar","Bar item 1", "b");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar",
|
||||
menuItemName: "Bar item 2",
|
||||
shortcutKeyEvent: { text: "B", isControl: true }
|
||||
});
|
||||
|
||||
Menu.addMenu("Bar > Spam");
|
||||
Menu.addMenuItem("Bar > Spam","Spam item 1");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Bar > Spam",
|
||||
menuItemName: "Spam item 2",
|
||||
isCheckable: true,
|
||||
isChecked: false
|
||||
});
|
||||
|
||||
Menu.addSeparator("Bar > Spam","Other Items");
|
||||
Menu.addMenuItem("Bar > Spam","Remove Spam item 2");
|
||||
Menu.addMenuItem("Foo","Remove Spam item 2");
|
||||
Menu.addSeparator("Bar > Spam","Other Items");
|
||||
Menu.addMenuItem("Bar > Spam","Remove Spam item 2");
|
||||
}
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Foo",
|
||||
menuItemName: "Remove Spam item 2"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "before Cut",
|
||||
beforeItem: "Cut"
|
||||
});
|
||||
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "after Nudge",
|
||||
afterItem: "Nudge"
|
||||
});
|
||||
if (Menu.menuItemExists("Edit","Cut")) {
|
||||
Window.alert("Menu Item Cut exist adding 'before Cut'.");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "before Cut",
|
||||
beforeItem: "Cut"
|
||||
});
|
||||
} else {
|
||||
Window.alert("Menu Item Cut doesn't exist!");
|
||||
}
|
||||
|
||||
if (Menu.menuItemExists("Edit","Nudge")) {
|
||||
Window.alert("Menu Item Nudge exist adding 'after Nudge'.");
|
||||
Menu.addMenuItem({
|
||||
menuName: "Edit",
|
||||
menuItemName: "after Nudge",
|
||||
afterItem: "Nudge"
|
||||
});
|
||||
} else {
|
||||
Window.alert("Menu Item Nudge doesn't exist!");
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
|
@ -82,6 +102,10 @@ function scriptEnding() {
|
|||
|
||||
Menu.removeMenu("Foo");
|
||||
Menu.removeMenu("Bar");
|
||||
|
||||
Menu.removeMenuItem("Edit", "before Cut");
|
||||
Menu.removeMenuItem("Edit", "after Nudge");
|
||||
|
||||
}
|
||||
|
||||
function menuItemEvent(menuItem) {
|
||||
|
|
|
@ -1,202 +0,0 @@
|
|||
//
|
||||
// particleBird.js
|
||||
// examples
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This sample script moves a voxel around like a bird and sometimes makes tweeting noises
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function vLength(v) {
|
||||
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||
}
|
||||
function printVector(v) {
|
||||
print(v.x + ", " + v.y + ", " + v.z + "\n");
|
||||
}
|
||||
// Create a random vector with individual lengths between a,b
|
||||
function randVector(a, b) {
|
||||
var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) };
|
||||
return rval;
|
||||
}
|
||||
|
||||
function vMinus(a, b) {
|
||||
var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
|
||||
return rval;
|
||||
}
|
||||
|
||||
function vPlus(a, b) {
|
||||
var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
|
||||
return rval;
|
||||
}
|
||||
|
||||
function vCopy(a, b) {
|
||||
a.x = b.x;
|
||||
a.y = b.y;
|
||||
a.z = b.z;
|
||||
return;
|
||||
}
|
||||
|
||||
// Returns a vector which is fraction of the way between a and b
|
||||
function vInterpolate(a, b, fraction) {
|
||||
var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction };
|
||||
return rval;
|
||||
}
|
||||
|
||||
// Decide what kind of bird we are
|
||||
var tweet;
|
||||
var color;
|
||||
var size;
|
||||
var which = Math.random();
|
||||
if (which < 0.2) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");
|
||||
color = { r: 100, g: 50, b: 120 };
|
||||
size = 0.08;
|
||||
} else if (which < 0.4) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/rosyfacedlovebird.raw");
|
||||
color = { r: 100, g: 150, b: 75 };
|
||||
size = 0.09;
|
||||
} else if (which < 0.6) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/saysphoebe.raw");
|
||||
color = { r: 84, g: 121, b: 36 };
|
||||
size = 0.05;
|
||||
} else if (which < 0.8) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw");
|
||||
color = { r: 23, g: 197, b: 230 };
|
||||
size = 0.12;
|
||||
} else {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/westernscreechowl.raw");
|
||||
color = { r: 50, g: 67, b: 144 };
|
||||
size = 0.15;
|
||||
}
|
||||
|
||||
|
||||
var startTimeInSeconds = new Date().getTime() / 1000;
|
||||
|
||||
var birdLifetime = 20; // lifetime of the bird in seconds!
|
||||
var position = { x: 0, y: 0, z: 0 };
|
||||
var targetPosition = { x: 0, y: 0, z: 0 };
|
||||
var range = 1.0; // Over what distance in meters do you want your bird to fly around
|
||||
var frame = 0;
|
||||
var moving = false;
|
||||
var tweeting = 0;
|
||||
var moved = false;
|
||||
|
||||
var CHANCE_OF_MOVING = 0.00;
|
||||
var CHANCE_OF_FLAPPING = 0.05;
|
||||
var CHANCE_OF_TWEETING = 0.05;
|
||||
var START_HEIGHT_ABOVE_ME = 1.5;
|
||||
var BIRD_GRAVITY = -0.1;
|
||||
var BIRD_FLAP = 1.0;
|
||||
var myPosition = MyAvatar.position;
|
||||
var properties = {
|
||||
lifetime: birdLifetime,
|
||||
position: { x: myPosition.x, y: myPosition.y + START_HEIGHT_ABOVE_ME, z: myPosition.z },
|
||||
velocity: { x: 0, y: Math.random() * BIRD_FLAP, z: 0 },
|
||||
gravity: { x: 0, y: BIRD_GRAVITY, z: 0 },
|
||||
radius : 0.1,
|
||||
color: { red: 0,
|
||||
green: 255,
|
||||
blue: 0 }
|
||||
};
|
||||
var range = 1.0; // Distance around avatar where I can move
|
||||
// Create the actual bird
|
||||
var particleID = Particles.addParticle(properties);
|
||||
function moveBird(deltaTime) {
|
||||
|
||||
// check to see if we've been running long enough that our bird is dead
|
||||
var nowTimeInSeconds = new Date().getTime() / 1000;
|
||||
if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) {
|
||||
|
||||
print("our bird is dying, stop our script");
|
||||
Script.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
myPosition = MyAvatar.position;
|
||||
frame++;
|
||||
if (frame % 3 == 0) {
|
||||
// Tweeting behavior
|
||||
if (tweeting == 0) {
|
||||
if (Math.random() < CHANCE_OF_TWEETING) {
|
||||
//print("tweet!" + "\n");
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = position;
|
||||
options.volume = 0.75;
|
||||
Audio.playSound(tweet, options);
|
||||
tweeting = 10;
|
||||
}
|
||||
} else {
|
||||
tweeting -= 1;
|
||||
}
|
||||
if (Math.random() < CHANCE_OF_FLAPPING) {
|
||||
// Add a little upward impulse to our bird
|
||||
// TODO: Get velocity
|
||||
//
|
||||
var newProperties = {
|
||||
velocity: { x:0.0, y: Math.random() * BIRD_FLAP, z: 0.0 }
|
||||
};
|
||||
Particles.editParticle(particleID, newProperties);
|
||||
print("flap!");
|
||||
}
|
||||
// Moving behavior
|
||||
if (moving == false) {
|
||||
if (Math.random() < CHANCE_OF_MOVING) {
|
||||
targetPosition = randVector(-range, range);
|
||||
targetPosition = vPlus(targetPosition, myPosition);
|
||||
|
||||
if (targetPosition.x < 0) {
|
||||
targetPosition.x = 0;
|
||||
}
|
||||
if (targetPosition.y < 0) {
|
||||
targetPosition.y = 0;
|
||||
}
|
||||
if (targetPosition.z < 0) {
|
||||
targetPosition.z = 0;
|
||||
}
|
||||
if (targetPosition.x > TREE_SCALE) {
|
||||
targetPosition.x = TREE_SCALE;
|
||||
}
|
||||
if (targetPosition.y > TREE_SCALE) {
|
||||
targetPosition.y = TREE_SCALE;
|
||||
}
|
||||
if (targetPosition.z > TREE_SCALE) {
|
||||
targetPosition.z = TREE_SCALE;
|
||||
}
|
||||
//printVector(position);
|
||||
moving = true;
|
||||
}
|
||||
}
|
||||
if (moving) {
|
||||
position = vInterpolate(position, targetPosition, 0.5);
|
||||
if (vLength(vMinus(position, targetPosition)) < (size / 5.0)) {
|
||||
moved = false;
|
||||
moving = false;
|
||||
} else {
|
||||
moved = true;
|
||||
}
|
||||
}
|
||||
if (moved || (tweeting > 0)) {
|
||||
if (tweeting > 0) {
|
||||
var newProperties = {
|
||||
position: position,
|
||||
radius : size * 1.5,
|
||||
color: { red: Math.random() * 255, green: 0, blue: 0 }
|
||||
};
|
||||
} else {
|
||||
var newProperties = {
|
||||
position: position,
|
||||
radius : size,
|
||||
color: { red: color.r, green: color.g, blue: color.b }
|
||||
};
|
||||
}
|
||||
Particles.editParticle(particleID, newProperties);
|
||||
moved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(moveBird);
|
199
examples/particleBirds.js
Normal file
199
examples/particleBirds.js
Normal file
|
@ -0,0 +1,199 @@
|
|||
//
|
||||
// particleBirds.js
|
||||
// examples
|
||||
//
|
||||
// Created by Benjamin Arnold on May 29, 2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// This sample script creates a swarm of tweeting bird particles that fly around the avatar.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// Multiply vector by scalar
|
||||
function vScalarMult(v, s) {
|
||||
var rval = { x: v.x * s, y: v.y * s, z: v.z * s };
|
||||
return rval;
|
||||
}
|
||||
|
||||
function printVector(v) {
|
||||
print(v.x + ", " + v.y + ", " + v.z + "\n");
|
||||
}
|
||||
// Create a random vector with individual lengths between a,b
|
||||
function randVector(a, b) {
|
||||
var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) };
|
||||
return rval;
|
||||
}
|
||||
|
||||
// Returns a vector which is fraction of the way between a and b
|
||||
function vInterpolate(a, b, fraction) {
|
||||
var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction };
|
||||
return rval;
|
||||
}
|
||||
|
||||
var startTimeInSeconds = new Date().getTime() / 1000;
|
||||
|
||||
var birdLifetime = 20; // lifetime of the birds in seconds!
|
||||
var range = 1.0; // Over what distance in meters do you want the flock to fly around
|
||||
var frame = 0;
|
||||
|
||||
var CHANCE_OF_MOVING = 0.1;
|
||||
var CHANCE_OF_TWEETING = 0.05;
|
||||
var BIRD_GRAVITY = -0.1;
|
||||
var BIRD_FLAP_SPEED = 10.0;
|
||||
var BIRD_VELOCITY = 0.5;
|
||||
var myPosition = MyAvatar.position;
|
||||
|
||||
var range = 1.0; // Distance around avatar where I can move
|
||||
|
||||
// This is our Bird object
|
||||
function Bird (particleID, tweetSound, targetPosition) {
|
||||
this.particleID = particleID;
|
||||
this.tweetSound = tweetSound;
|
||||
this.previousFlapOffset = 0;
|
||||
this.targetPosition = targetPosition;
|
||||
this.moving = false;
|
||||
this.tweeting = -1;
|
||||
}
|
||||
|
||||
// Array of birds
|
||||
var birds = [];
|
||||
|
||||
function addBird()
|
||||
{
|
||||
// Decide what kind of bird we are
|
||||
var tweet;
|
||||
var color;
|
||||
var size;
|
||||
var which = Math.random();
|
||||
if (which < 0.2) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw");
|
||||
color = { red: 100, green: 50, blue: 120 };
|
||||
size = 0.08;
|
||||
} else if (which < 0.4) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/rosyfacedlovebird.raw");
|
||||
color = { red: 100, green: 150, blue: 75 };
|
||||
size = 0.09;
|
||||
} else if (which < 0.6) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/saysphoebe.raw");
|
||||
color = { red: 84, green: 121, blue: 36 };
|
||||
size = 0.05;
|
||||
} else if (which < 0.8) {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw");
|
||||
color = { red: 23, green: 197, blue: 230 };
|
||||
size = 0.12;
|
||||
} else {
|
||||
tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/westernscreechowl.raw");
|
||||
color = { red: 50, green: 67, blue: 144 };
|
||||
size = 0.15;
|
||||
}
|
||||
var properties = {
|
||||
lifetime: birdLifetime,
|
||||
position: Vec3.sum(randVector(-range, range), myPosition),
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: BIRD_GRAVITY, z: 0 },
|
||||
radius : size,
|
||||
color: color
|
||||
};
|
||||
|
||||
birds.push(new Bird(Particles.addParticle(properties), tweet, properties.position));
|
||||
}
|
||||
|
||||
var numBirds = 30;
|
||||
|
||||
// Generate the birds
|
||||
for (var i = 0; i < numBirds; i++) {
|
||||
addBird();
|
||||
}
|
||||
|
||||
// Main update function
|
||||
function updateBirds(deltaTime) {
|
||||
|
||||
// Check to see if we've been running long enough that our birds are dead
|
||||
var nowTimeInSeconds = new Date().getTime() / 1000;
|
||||
if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) {
|
||||
|
||||
print("our birds are dying, stop our script");
|
||||
Script.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
frame++;
|
||||
// Only update every third frame
|
||||
if ((frame % 3) == 0) {
|
||||
myPosition = MyAvatar.position;
|
||||
|
||||
// Update all the birds
|
||||
for (var i = 0; i < numBirds; i++) {
|
||||
particleID = birds[i].particleID;
|
||||
var properties = Particles.getParticleProperties(particleID);
|
||||
|
||||
// Tweeting behavior
|
||||
if (birds[i].tweeting == 0) {
|
||||
if (Math.random() < CHANCE_OF_TWEETING) {
|
||||
var options = new AudioInjectionOptions();
|
||||
options.position = properties.position;
|
||||
options.volume = 0.75;
|
||||
Audio.playSound(birds[i].tweetSound, options);
|
||||
birds[i].tweeting = 10;
|
||||
}
|
||||
} else {
|
||||
birds[i].tweeting -= 1;
|
||||
}
|
||||
|
||||
// Begin movement by getting a target
|
||||
if (birds[i].moving == false) {
|
||||
if (Math.random() < CHANCE_OF_MOVING) {
|
||||
var targetPosition = Vec3.sum(randVector(-range, range), myPosition);
|
||||
|
||||
if (targetPosition.x < 0) {
|
||||
targetPosition.x = 0;
|
||||
}
|
||||
if (targetPosition.y < 0) {
|
||||
targetPosition.y = 0;
|
||||
}
|
||||
if (targetPosition.z < 0) {
|
||||
targetPosition.z = 0;
|
||||
}
|
||||
if (targetPosition.x > TREE_SCALE) {
|
||||
targetPosition.x = TREE_SCALE;
|
||||
}
|
||||
if (targetPosition.y > TREE_SCALE) {
|
||||
targetPosition.y = TREE_SCALE;
|
||||
}
|
||||
if (targetPosition.z > TREE_SCALE) {
|
||||
targetPosition.z = TREE_SCALE;
|
||||
}
|
||||
|
||||
birds[i].targetPosition = targetPosition;
|
||||
|
||||
birds[i].moving = true;
|
||||
}
|
||||
}
|
||||
// If we are moving, move towards the target
|
||||
if (birds[i].moving) {
|
||||
var desiredVelocity = Vec3.subtract(birds[i].targetPosition, properties.position);
|
||||
desiredVelocity = vScalarMult(Vec3.normalize(desiredVelocity), BIRD_VELOCITY);
|
||||
|
||||
properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.2);
|
||||
// If we are near the target, we should get a new target
|
||||
if (Vec3.length(Vec3.subtract(properties.position, birds[i].targetPosition)) < (properties.radius / 5.0)) {
|
||||
birds[i].moving = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Use a cosine wave offset to make it look like its flapping.
|
||||
var offset = Math.cos(nowTimeInSeconds * BIRD_FLAP_SPEED) * properties.radius;
|
||||
properties.position.y = properties.position.y + (offset - birds[i].previousFlapOffset);
|
||||
// Change position relative to previous offset.
|
||||
birds[i].previousFlapOffset = offset;
|
||||
|
||||
// Update the particle
|
||||
Particles.editParticle(particleID, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register the call back so it fires before each data send
|
||||
Script.update.connect(updateBirds);
|
|
@ -169,7 +169,7 @@ Controller.keyPressEvent.connect(keyPressEvent);
|
|||
Script.scriptEnding.connect(function() {
|
||||
|
||||
for (var i = 0; i < pose.length; i++){
|
||||
MyAvatar.clearJointData(pose[i][0]);
|
||||
MyAvatar.clearJointData(pose[i].joint);
|
||||
}
|
||||
|
||||
Overlays.deleteOverlay(sitDownButton);
|
||||
|
|
|
@ -2789,16 +2789,19 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
|
||||
glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation();
|
||||
|
||||
// get the eye positions relative to the neck and use them to set the face translation
|
||||
glm::vec3 leftEyePosition, rightEyePosition;
|
||||
_myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3());
|
||||
_myAvatar->getHead()->getFaceModel().getEyePositions(leftEyePosition, rightEyePosition);
|
||||
_myAvatar->getHead()->getFaceModel().setTranslation((leftEyePosition + rightEyePosition) * -0.5f);
|
||||
|
||||
// get the neck position relative to the body and use it to set the skeleton translation
|
||||
// get the neck position so we can translate the face relative to it
|
||||
glm::vec3 neckPosition;
|
||||
_myAvatar->getSkeletonModel().setTranslation(glm::vec3());
|
||||
_myAvatar->getSkeletonModel().getNeckPosition(neckPosition);
|
||||
|
||||
// get the eye position relative to the body
|
||||
glm::vec3 eyePosition = _myAvatar->getHead()->calculateAverageEyePosition();
|
||||
float eyeHeight = eyePosition.y - _myAvatar->getPosition().y;
|
||||
|
||||
// set the translation of the face relative to the neck position
|
||||
_myAvatar->getHead()->getFaceModel().setTranslation(neckPosition - glm::vec3(0, eyeHeight, 0));
|
||||
|
||||
// translate the neck relative to the face
|
||||
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
|
||||
neckPosition);
|
||||
|
||||
|
|
|
@ -858,6 +858,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
|
|||
|
||||
void Menu::removeAction(QMenu* menu, const QString& actionName) {
|
||||
menu->removeAction(_actionHash.value(actionName));
|
||||
_actionHash.remove(actionName);
|
||||
}
|
||||
|
||||
void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
||||
|
@ -1650,6 +1651,16 @@ void Menu::removeMenu(const QString& menuName) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Menu::menuExists(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu::addSeparator(const QString& menuName, const QString& separatorName) {
|
||||
QMenu* menuObj = getMenu(menuName);
|
||||
if (menuObj) {
|
||||
|
@ -1725,8 +1736,17 @@ void Menu::removeMenuItem(const QString& menu, const QString& menuitem) {
|
|||
QMenu* menuObj = getMenu(menu);
|
||||
if (menuObj) {
|
||||
removeAction(menuObj, menuitem);
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
QMenuBar::repaint();
|
||||
};
|
||||
|
||||
bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
|
||||
QMenu* menuObj = getMenu(menu);
|
||||
QAction* menuItemAction = _actionHash.value(menuitem);
|
||||
if (menuObj && menuItemAction) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
QString Menu::getSnapshotsLocation() const {
|
||||
|
|
|
@ -169,10 +169,12 @@ public slots:
|
|||
|
||||
QMenu* addMenu(const QString& menuName);
|
||||
void removeMenu(const QString& menuName);
|
||||
bool menuExists(const QString& menuName);
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
void addMenuItem(const MenuItemProperties& properties);
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
bool isOptionChecked(const QString& menuOption) const;
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(_rotation);
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState._transform * glm::translate(state._translation) *
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState._transform * glm::translate(state.getDefaultTranslationInParentFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||
state._rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
|
||||
* glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1]))
|
||||
|
@ -59,7 +59,7 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
|
|||
|
||||
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
// likewise with the eye joints
|
||||
glm::mat4 inverse = glm::inverse(parentState._transform * glm::translate(state._translation) *
|
||||
glm::mat4 inverse = glm::inverse(parentState._transform * glm::translate(state.getDefaultTranslationInParentFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation));
|
||||
glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientation() * IDENTITY_FRONT, 0.0f));
|
||||
glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(_owningHead->getLookAtPosition() +
|
||||
|
|
|
@ -45,7 +45,8 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
}
|
||||
int jointIndex = geometry.humanIKJointIndices.at(humanIKJointIndex);
|
||||
if (jointIndex != -1) {
|
||||
setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), PALM_PRIORITY);
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
state.setRotation(_rotation * prioVR->getJointRotations().at(i), PALM_PRIORITY);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -131,7 +132,7 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) {
|
|||
}
|
||||
|
||||
void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) {
|
||||
if (jointIndex == -1) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return;
|
||||
}
|
||||
setJointPosition(jointIndex, position, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
|
@ -145,17 +146,16 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
|
|||
if (forearmLength < EPSILON) {
|
||||
return;
|
||||
}
|
||||
glm::quat handRotation;
|
||||
getJointRotation(jointIndex, handRotation, true);
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
glm::quat handRotation = state.getJointRotation(true);
|
||||
|
||||
// align hand with forearm
|
||||
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
|
||||
applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f),
|
||||
forearmVector), true, PALM_PRIORITY);
|
||||
state.applyRotationDelta(rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), true, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||
if (jointIndex == -1) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
@ -169,9 +169,11 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
|||
glm::quat palmRotation;
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK) &&
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||
getJointRotation(parentJointIndex, palmRotation, true);
|
||||
JointState parentState = _jointStates[parentJointIndex];
|
||||
palmRotation = parentState.getJointRotation(true);
|
||||
} else {
|
||||
getJointRotation(jointIndex, palmRotation, true);
|
||||
JointState state = _jointStates[jointIndex];
|
||||
palmRotation = state.getJointRotation(true);
|
||||
}
|
||||
palmRotation = rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()) * palmRotation;
|
||||
|
||||
|
@ -188,7 +190,9 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
|||
setJointPosition(parentJointIndex, palm.getPosition() + forearmVector *
|
||||
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale),
|
||||
glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
||||
setJointRotation(parentJointIndex, palmRotation, PALM_PRIORITY);
|
||||
JointState& parentState = _jointStates[parentJointIndex];
|
||||
parentState.setRotation(palmRotation, PALM_PRIORITY);
|
||||
// slam parent-relative rotation to identity
|
||||
_jointStates[jointIndex]._rotation = glm::quat();
|
||||
|
||||
} else {
|
||||
|
@ -229,7 +233,7 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
|
|||
}
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(_rotation);
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState._transform * glm::translate(state._translation) *
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState._transform * glm::translate(state.getDefaultTranslationInParentFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
|
||||
state._rotation = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(),
|
||||
glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(),
|
||||
|
@ -245,7 +249,7 @@ void SkeletonModel::maybeUpdateEyeRotation(const JointState& parentState, const
|
|||
}
|
||||
|
||||
void SkeletonModel::renderJointConstraints(int jointIndex) {
|
||||
if (jointIndex == -1) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
@ -351,36 +355,29 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c
|
|||
|
||||
// ik solution
|
||||
glm::vec3 elbowPosition = shoulderPosition + glm::normalize(shoulderToWrist) * mid - tangent * height;
|
||||
|
||||
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
|
||||
|
||||
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
|
||||
setJointRotation(shoulderJointIndex, shoulderRotation, PALM_PRIORITY);
|
||||
|
||||
JointState& shoulderState = _jointStates[shoulderJointIndex];
|
||||
shoulderState.setRotation(shoulderRotation, PALM_PRIORITY);
|
||||
|
||||
setJointRotation(elbowJointIndex, rotationBetween(shoulderRotation * forwardVector,
|
||||
wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY);
|
||||
JointState& elbowState = _jointStates[elbowJointIndex];
|
||||
elbowState.setRotation(rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * shoulderRotation, PALM_PRIORITY);
|
||||
|
||||
setJointRotation(jointIndex, rotation, PALM_PRIORITY);
|
||||
JointState& handState = _jointStates[jointIndex];
|
||||
handState.setRotation(rotation, PALM_PRIORITY);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {
|
||||
return getJointPosition(getLeftHandJointIndex(), position);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getLeftHandRotation(glm::quat& rotation) const {
|
||||
return getJointRotation(getLeftHandJointIndex(), rotation);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getRightHandPosition(glm::vec3& position) const {
|
||||
return getJointPosition(getRightHandJointIndex(), position);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getRightHandRotation(glm::quat& rotation) const {
|
||||
return getJointRotation(getRightHandJointIndex(), rotation);
|
||||
}
|
||||
|
||||
bool SkeletonModel::restoreLeftHandPosition(float percent, float priority) {
|
||||
return restoreJointPosition(getLeftHandJointIndex(), percent, priority);
|
||||
bool SkeletonModel::restoreLeftHandPosition(float fraction, float priority) {
|
||||
return restoreJointPosition(getLeftHandJointIndex(), fraction, priority);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getLeftShoulderPosition(glm::vec3& position) const {
|
||||
|
@ -391,8 +388,8 @@ float SkeletonModel::getLeftArmLength() const {
|
|||
return getLimbLength(getLeftHandJointIndex());
|
||||
}
|
||||
|
||||
bool SkeletonModel::restoreRightHandPosition(float percent, float priority) {
|
||||
return restoreJointPosition(getRightHandJointIndex(), percent, priority);
|
||||
bool SkeletonModel::restoreRightHandPosition(float fraction, float priority) {
|
||||
return restoreJointPosition(getRightHandJointIndex(), fraction, priority);
|
||||
}
|
||||
|
||||
bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const {
|
||||
|
|
|
@ -45,22 +45,14 @@ public:
|
|||
/// \return true whether or not the position was found
|
||||
bool getLeftHandPosition(glm::vec3& position) const;
|
||||
|
||||
/// Retrieve the rotation of the left hand
|
||||
/// \return true whether or not the rotation was found
|
||||
bool getLeftHandRotation(glm::quat& rotation) const;
|
||||
|
||||
/// Retrieve the position of the right hand
|
||||
/// \return true whether or not the position was found
|
||||
bool getRightHandPosition(glm::vec3& position) const;
|
||||
|
||||
/// Retrieve the rotation of the right hand
|
||||
/// \return true whether or not the rotation was found
|
||||
bool getRightHandRotation(glm::quat& rotation) const;
|
||||
|
||||
/// Restores some percentage of the default position of the left hand.
|
||||
/// \param percent the percentage of the default position to restore
|
||||
/// Restores some fraction of the default position of the left hand.
|
||||
/// \param fraction the fraction of the default position to restore
|
||||
/// \return whether or not the left hand joint was found
|
||||
bool restoreLeftHandPosition(float percent = 1.0f, float priority = 1.0f);
|
||||
bool restoreLeftHandPosition(float fraction = 1.0f, float priority = 1.0f);
|
||||
|
||||
/// Gets the position of the left shoulder.
|
||||
/// \return whether or not the left shoulder joint was found
|
||||
|
@ -69,10 +61,10 @@ public:
|
|||
/// Returns the extended length from the left hand to its last free ancestor.
|
||||
float getLeftArmLength() const;
|
||||
|
||||
/// Restores some percentage of the default position of the right hand.
|
||||
/// \param percent the percentage of the default position to restore
|
||||
/// Restores some fraction of the default position of the right hand.
|
||||
/// \param fraction the fraction of the default position to restore
|
||||
/// \return whether or not the right hand joint was found
|
||||
bool restoreRightHandPosition(float percent = 1.0f, float priority = 1.0f);
|
||||
bool restoreRightHandPosition(float fraction = 1.0f, float priority = 1.0f);
|
||||
|
||||
/// Gets the position of the right shoulder.
|
||||
/// \return whether or not the right shoulder joint was found
|
||||
|
|
|
@ -41,9 +41,9 @@ Model::Model(QObject* parent) :
|
|||
_snappedToCenter(false),
|
||||
_rootIndex(-1),
|
||||
_shapesAreDirty(true),
|
||||
_boundingRadius(0.f),
|
||||
_boundingShape(),
|
||||
_boundingShapeLocalOffset(0.f),
|
||||
_boundingRadius(0.0f),
|
||||
_boundingShape(),
|
||||
_boundingShapeLocalOffset(0.0f),
|
||||
_lodDistance(0.0f),
|
||||
_pupilDilation(0.0f),
|
||||
_url("http://invalid.com") {
|
||||
|
@ -125,7 +125,7 @@ void Model::setScale(const glm::vec3& scale) {
|
|||
void Model::setScaleInternal(const glm::vec3& scale) {
|
||||
float scaleLength = glm::length(_scale);
|
||||
float relativeDeltaScale = glm::length(_scale - scale) / scaleLength;
|
||||
|
||||
|
||||
const float ONE_PERCENT = 0.01f;
|
||||
if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) {
|
||||
_scale = scale;
|
||||
|
@ -186,12 +186,12 @@ QVector<JointState> Model::createJointStates(const FBXGeometry& geometry) {
|
|||
if (parentIndex == -1) {
|
||||
_rootIndex = i;
|
||||
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
state.updateWorldTransform(baseTransform, _rotation);
|
||||
state.computeTransforms(baseTransform, _rotation);
|
||||
++numJointsSet;
|
||||
jointIsSet[i] = true;
|
||||
} else if (jointIsSet[parentIndex]) {
|
||||
const JointState& parentState = jointStates.at(parentIndex);
|
||||
state.updateWorldTransform(parentState._transform, parentState._combinedRotation);
|
||||
state.computeTransforms(parentState._transform, parentState._combinedRotation);
|
||||
++numJointsSet;
|
||||
jointIsSet[i] = true;
|
||||
}
|
||||
|
@ -660,8 +660,8 @@ Extents Model::getMeshExtents() const {
|
|||
|
||||
// even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which
|
||||
// is captured in the offset matrix
|
||||
glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0));
|
||||
glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0));
|
||||
glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f));
|
||||
glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f));
|
||||
Extents scaledExtents = { minimum * _scale, maximum * _scale };
|
||||
return scaledExtents;
|
||||
}
|
||||
|
@ -675,8 +675,8 @@ Extents Model::getUnscaledMeshExtents() const {
|
|||
|
||||
// even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which
|
||||
// is captured in the offset matrix
|
||||
glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0));
|
||||
glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0));
|
||||
glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f));
|
||||
glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f));
|
||||
Extents scaledExtents = { minimum, maximum };
|
||||
|
||||
return scaledExtents;
|
||||
|
@ -701,9 +701,8 @@ void Model::setJointState(int index, bool valid, const glm::quat& rotation, floa
|
|||
if (valid) {
|
||||
state._rotation = rotation;
|
||||
state._animationPriority = priority;
|
||||
} else if (priority == state._animationPriority) {
|
||||
state._rotation = _geometry->getFBXGeometry().joints.at(index).rotation;
|
||||
state._animationPriority = 0.0f;
|
||||
} else {
|
||||
state.restoreRotation(1.0f, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -733,7 +732,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
|
|||
}
|
||||
|
||||
bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
position = _translation + extractTranslation(_jointStates[jointIndex]._transform);
|
||||
|
@ -741,17 +740,15 @@ bool Model::getJointPosition(int jointIndex, glm::vec3& position) const {
|
|||
}
|
||||
|
||||
bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) const {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
rotation = _jointStates[jointIndex]._combinedRotation *
|
||||
(fromBind ? _geometry->getFBXGeometry().joints[jointIndex].inverseBindRotation :
|
||||
_geometry->getFBXGeometry().joints[jointIndex].inverseDefaultRotation);
|
||||
rotation = _jointStates[jointIndex].getJointRotation(fromBind);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
rotation = _jointStates[jointIndex]._combinedRotation;
|
||||
|
@ -821,7 +818,7 @@ void Model::rebuildShapes() {
|
|||
// however we must have a shape for each joint,
|
||||
// so we make a bogus sphere with zero radius.
|
||||
// TODO: implement collision groups for more control over what collides with what
|
||||
SphereShape* sphere = new SphereShape(0.f, glm::vec3(0.0f));
|
||||
SphereShape* sphere = new SphereShape(0.0f, glm::vec3(0.0f));
|
||||
_jointShapes.push_back(sphere);
|
||||
}
|
||||
}
|
||||
|
@ -963,8 +960,8 @@ void Model::resetShapePositions() {
|
|||
|
||||
void Model::updateShapePositions() {
|
||||
if (_shapesAreDirty && _jointShapes.size() == _jointStates.size()) {
|
||||
glm::vec3 rootPosition(0.f);
|
||||
_boundingRadius = 0.f;
|
||||
glm::vec3 rootPosition(0.0f);
|
||||
_boundingRadius = 0.0f;
|
||||
float uniformScale = extractUniformScale(_scale);
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
|
@ -1190,13 +1187,14 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
|
|||
}
|
||||
|
||||
void Model::simulateInternal(float deltaTime) {
|
||||
// NOTE: this is a recursive call that walks all attachments, and their attachments
|
||||
// update the world space transforms for all joints
|
||||
|
||||
// update animations
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
handle->simulate(deltaTime);
|
||||
}
|
||||
|
||||
// NOTE: this is a recursive call that walks all attachments, and their attachments
|
||||
// update the world space transforms for all joints
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i);
|
||||
}
|
||||
|
@ -1244,10 +1242,10 @@ void Model::updateJointState(int index) {
|
|||
if (joint.parentIndex == -1) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
state.updateWorldTransform(baseTransform, _rotation);
|
||||
state.computeTransforms(baseTransform, _rotation);
|
||||
} else {
|
||||
const JointState& parentState = _jointStates.at(joint.parentIndex);
|
||||
state.updateWorldTransform(parentState._transform, parentState._combinedRotation);
|
||||
state.computeTransforms(parentState._transform, parentState._combinedRotation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1274,9 +1272,12 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
|
|||
// first, try to rotate the end effector as close as possible to the target rotation, if any
|
||||
glm::quat endRotation;
|
||||
if (useRotation) {
|
||||
getJointRotation(jointIndex, endRotation, true);
|
||||
applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation), true, priority);
|
||||
getJointRotation(jointIndex, endRotation, true);
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
|
||||
// TODO: figure out what this is trying to do and combine it into one JointState method
|
||||
endRotation = state.getJointRotation(true);
|
||||
state.applyRotationDelta(rotation * glm::inverse(endRotation), true, priority);
|
||||
endRotation = state.getJointRotation(true);
|
||||
}
|
||||
|
||||
// then, we go from the joint upwards, rotating the end as close as possible to the target
|
||||
|
@ -1319,7 +1320,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
|
|||
1.0f / (combinedWeight + 1.0f));
|
||||
}
|
||||
}
|
||||
applyRotationDelta(index, combinedDelta, true, priority);
|
||||
state.applyRotationDelta(combinedDelta, true, priority);
|
||||
glm::quat actualDelta = state._combinedRotation * glm::inverse(oldCombinedRotation);
|
||||
endPosition = actualDelta * jointVector + jointPosition;
|
||||
if (useRotation) {
|
||||
|
@ -1337,37 +1338,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, float priority) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
if (priority >= state._animationPriority) {
|
||||
state._rotation = state._rotation * glm::inverse(state._combinedRotation) * rotation *
|
||||
glm::inverse(_geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation);
|
||||
state._animationPriority = priority;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Model::setJointTranslation(int jointIndex, const glm::vec3& translation) {
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
|
||||
glm::mat4 parentTransform;
|
||||
if (joint.parentIndex == -1) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
parentTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
|
||||
} else {
|
||||
parentTransform = _jointStates.at(joint.parentIndex)._transform;
|
||||
}
|
||||
glm::vec3 preTranslation = extractTranslation(joint.preTransform * glm::mat4_cast(joint.preRotation *
|
||||
state._rotation * joint.postRotation) * joint.postTransform);
|
||||
state._translation = glm::vec3(glm::inverse(parentTransform) * glm::vec4(translation, 1.0f)) - preTranslation;
|
||||
}
|
||||
|
||||
bool Model::restoreJointPosition(int jointIndex, float percent, float priority) {
|
||||
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
|
||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1376,12 +1347,7 @@ bool Model::restoreJointPosition(int jointIndex, float percent, float priority)
|
|||
|
||||
foreach (int index, freeLineage) {
|
||||
JointState& state = _jointStates[index];
|
||||
if (priority == state._animationPriority) {
|
||||
const FBXJoint& joint = geometry.joints.at(index);
|
||||
state._rotation = safeMix(state._rotation, joint.rotation, percent);
|
||||
state._translation = glm::mix(state._translation, joint.translation, percent);
|
||||
state._animationPriority = 0.0f;
|
||||
}
|
||||
state.restoreRotation(fraction, priority);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1400,27 +1366,6 @@ float Model::getLimbLength(int jointIndex) const {
|
|||
return length;
|
||||
}
|
||||
|
||||
void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) {
|
||||
JointState& state = _jointStates[jointIndex];
|
||||
if (priority < state._animationPriority) {
|
||||
return;
|
||||
}
|
||||
state._animationPriority = priority;
|
||||
const FBXJoint& joint = state.getFBXJoint();
|
||||
if (!constrain || (joint.rotationMin == glm::vec3(-PI, -PI, -PI) &&
|
||||
joint.rotationMax == glm::vec3(PI, PI, PI))) {
|
||||
// no constraints
|
||||
state._rotation = state._rotation * glm::inverse(state._combinedRotation) * delta * state._combinedRotation;
|
||||
state._combinedRotation = delta * state._combinedRotation;
|
||||
return;
|
||||
}
|
||||
glm::quat targetRotation = delta * state._combinedRotation;
|
||||
glm::vec3 eulers = safeEulerAngles(state._rotation * glm::inverse(state._combinedRotation) * targetRotation);
|
||||
glm::quat newRotation = glm::quat(glm::clamp(eulers, joint.rotationMin, joint.rotationMax));
|
||||
state._combinedRotation = state._combinedRotation * glm::inverse(state._rotation) * newRotation;
|
||||
state._rotation = newRotation;
|
||||
}
|
||||
|
||||
const int BALL_SUBDIVISIONS = 10;
|
||||
|
||||
void Model::renderJointCollisionShapes(float alpha) {
|
||||
|
@ -1463,7 +1408,7 @@ void Model::renderJointCollisionShapes(float alpha) {
|
|||
glutSolidSphere(capsule->getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS);
|
||||
|
||||
// draw a green cylinder between the two points
|
||||
glm::vec3 origin(0.f);
|
||||
glm::vec3 origin(0.0f);
|
||||
glColor4f(0.6f, 0.8f, 0.6f, alpha);
|
||||
Avatar::renderJointConnectingCone( origin, axis, capsule->getRadius(), capsule->getRadius());
|
||||
}
|
||||
|
@ -1495,7 +1440,7 @@ void Model::renderBoundingCollisionShapes(float alpha) {
|
|||
glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS);
|
||||
|
||||
// draw a green cylinder between the two points
|
||||
glm::vec3 origin(0.f);
|
||||
glm::vec3 origin(0.0f);
|
||||
glColor4f(0.6f, 0.8f, 0.6f, alpha);
|
||||
Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius());
|
||||
|
||||
|
@ -1523,7 +1468,7 @@ void Model::applyCollision(CollisionInfo& collision) {
|
|||
return;
|
||||
}
|
||||
|
||||
glm::vec3 jointPosition(0.f);
|
||||
glm::vec3 jointPosition(0.0f);
|
||||
int jointIndex = collision._intData;
|
||||
if (getJointPosition(jointIndex, jointPosition)) {
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
|
||||
|
@ -2039,31 +1984,75 @@ void AnimationHandle::replaceMatchingPriorities(float newPriority) {
|
|||
// JointState TODO: move this class to its own files
|
||||
// ----------------------------------------------------------------------------
|
||||
JointState::JointState() :
|
||||
_translation(0.0f),
|
||||
_animationPriority(0.0f),
|
||||
_fbxJoint(NULL) {
|
||||
}
|
||||
|
||||
void JointState::setFBXJoint(const FBXJoint* joint) {
|
||||
void JointState::setFBXJoint(const FBXJoint* joint) {
|
||||
assert(joint != NULL);
|
||||
_translation = joint->translation;
|
||||
_rotation = joint->rotation;
|
||||
// NOTE: JointState does not own the FBXJoint to which it points.
|
||||
_fbxJoint = joint;
|
||||
}
|
||||
|
||||
void JointState::updateWorldTransform(const glm::mat4& baseTransform, const glm::quat& parentRotation) {
|
||||
assert(_fbxJoint != NULL);
|
||||
glm::quat combinedRotation = _fbxJoint->preRotation * _rotation * _fbxJoint->postRotation;
|
||||
_transform = baseTransform * glm::translate(_translation) * _fbxJoint->preTransform * glm::mat4_cast(combinedRotation) * _fbxJoint->postTransform;
|
||||
_combinedRotation = parentRotation * combinedRotation;
|
||||
}
|
||||
|
||||
void JointState::copyState(const JointState& state) {
|
||||
_translation = state._translation;
|
||||
_rotation = state._rotation;
|
||||
_transform = state._transform;
|
||||
_combinedRotation = state._combinedRotation;
|
||||
_animationPriority = state._animationPriority;
|
||||
// DO NOT copy _fbxJoint
|
||||
}
|
||||
|
||||
void JointState::computeTransforms(const glm::mat4& baseTransform, const glm::quat& baseRotation) {
|
||||
assert(_fbxJoint != NULL);
|
||||
glm::quat combinedRotation = _fbxJoint->preRotation * _rotation * _fbxJoint->postRotation;
|
||||
_transform = baseTransform * glm::translate(_fbxJoint->translation) * _fbxJoint->preTransform
|
||||
* glm::mat4_cast(combinedRotation) * _fbxJoint->postTransform;
|
||||
_combinedRotation = baseRotation * combinedRotation;
|
||||
}
|
||||
|
||||
glm::quat JointState::getJointRotation(bool fromBind) const {
|
||||
assert(_fbxJoint != NULL);
|
||||
return _combinedRotation * (fromBind ? _fbxJoint->inverseBindRotation : _fbxJoint->inverseDefaultRotation);
|
||||
}
|
||||
|
||||
void JointState::restoreRotation(float fraction, float priority) {
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority == _animationPriority) {
|
||||
_rotation = safeMix(_rotation, _fbxJoint->rotation, fraction);
|
||||
_animationPriority = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::setRotation(const glm::quat& rotation, float priority) {
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority >= _animationPriority) {
|
||||
_rotation = _rotation * glm::inverse(_combinedRotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation);
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) {
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority < _animationPriority) {
|
||||
return;
|
||||
}
|
||||
_animationPriority = priority;
|
||||
if (!constrain || (_fbxJoint->rotationMin == glm::vec3(-PI, -PI, -PI) &&
|
||||
_fbxJoint->rotationMax == glm::vec3(PI, PI, PI))) {
|
||||
// no constraints
|
||||
_rotation = _rotation * glm::inverse(_combinedRotation) * delta * _combinedRotation;
|
||||
_combinedRotation = delta * _combinedRotation;
|
||||
return;
|
||||
}
|
||||
glm::quat targetRotation = delta * _combinedRotation;
|
||||
glm::vec3 eulers = safeEulerAngles(_rotation * glm::inverse(_combinedRotation) * targetRotation);
|
||||
glm::quat newRotation = glm::quat(glm::clamp(eulers, _fbxJoint->rotationMin, _fbxJoint->rotationMax));
|
||||
_combinedRotation = _combinedRotation * glm::inverse(_rotation) * newRotation;
|
||||
_rotation = newRotation;
|
||||
}
|
||||
|
||||
const glm::vec3& JointState::getDefaultTranslationInParentFrame() const {
|
||||
assert(_fbxJoint != NULL);
|
||||
return _fbxJoint->translation;
|
||||
}
|
||||
|
|
|
@ -38,11 +38,24 @@ public:
|
|||
void setFBXJoint(const FBXJoint* joint);
|
||||
const FBXJoint& getFBXJoint() const { return *_fbxJoint; }
|
||||
|
||||
void updateWorldTransform(const glm::mat4& baseTransform, const glm::quat& parentRotation);
|
||||
|
||||
void copyState(const JointState& state);
|
||||
|
||||
glm::vec3 _translation; // translation relative to parent
|
||||
/// computes new _transform and _combinedRotation
|
||||
void computeTransforms(const glm::mat4& baseTransform, const glm::quat& baseRotation);
|
||||
|
||||
/// \return rotation from the joint's default (or bind) frame to world frame
|
||||
glm::quat getJointRotation(bool fromBind = false) const;
|
||||
|
||||
void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f);
|
||||
|
||||
const glm::vec3& getDefaultTranslationInParentFrame() const;
|
||||
|
||||
void restoreRotation(float fraction, float priority);
|
||||
|
||||
/// \param rotation is from bind- to world-frame
|
||||
/// computes parent relative _rotation and sets that
|
||||
void setRotation(const glm::quat& rotation, float priority);
|
||||
|
||||
glm::quat _rotation; // rotation relative to parent
|
||||
glm::mat4 _transform; // rotation to world frame + translation in model frame
|
||||
glm::quat _combinedRotation; // rotation from joint local to world frame
|
||||
|
@ -234,22 +247,17 @@ protected:
|
|||
bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(),
|
||||
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
|
||||
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f);
|
||||
bool setJointRotation(int jointIndex, const glm::quat& rotation, float priority = 1.0f);
|
||||
|
||||
void setJointTranslation(int jointIndex, const glm::vec3& translation);
|
||||
|
||||
/// Restores the indexed joint to its default position.
|
||||
/// \param percent the percentage of the default position to apply (i.e., 0.25f to slerp one fourth of the way to
|
||||
/// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to
|
||||
/// the original position
|
||||
/// \return true if the joint was found
|
||||
bool restoreJointPosition(int jointIndex, float percent = 1.0f, float priority = 0.0f);
|
||||
bool restoreJointPosition(int jointIndex, float fraction = 1.0f, float priority = 0.0f);
|
||||
|
||||
/// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's
|
||||
/// first free ancestor.
|
||||
float getLimbLength(int jointIndex) const;
|
||||
|
||||
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true, float priority = 1.0f);
|
||||
|
||||
void computeBoundingShape(const FBXGeometry& geometry);
|
||||
|
||||
private:
|
||||
|
|
|
@ -35,6 +35,14 @@ void MenuScriptingInterface::removeMenu(const QString& menu) {
|
|||
QMetaObject::invokeMethod(Menu::getInstance(), "removeMenu", Q_ARG(const QString&, menu));
|
||||
}
|
||||
|
||||
bool MenuScriptingInterface::menuExists(const QString& menu) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, result),
|
||||
Q_ARG(const QString&, menu));
|
||||
return result;
|
||||
}
|
||||
|
||||
void MenuScriptingInterface::addSeparator(const QString& menuName, const QString& separatorName) {
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "addSeparator",
|
||||
Q_ARG(const QString&, menuName),
|
||||
|
@ -67,6 +75,15 @@ void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString&
|
|||
Q_ARG(const QString&, menuitem));
|
||||
};
|
||||
|
||||
bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, result),
|
||||
Q_ARG(const QString&, menu),
|
||||
Q_ARG(const QString&, menuitem));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
|
||||
|
|
|
@ -33,6 +33,7 @@ private slots:
|
|||
public slots:
|
||||
void addMenu(const QString& menuName);
|
||||
void removeMenu(const QString& menuName);
|
||||
bool menuExists(const QString& menuName);
|
||||
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
@ -42,6 +43,7 @@ public slots:
|
|||
void addMenuItem(const QString& menuName, const QString& menuitem);
|
||||
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
|
Loading…
Reference in a new issue