mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 10:47:11 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into death_to_dot_f
Conflicts: interface/src/avatar/Avatar.cpp interface/src/avatar/SkeletonModel.cpp
This commit is contained in:
commit
4d5451fafe
41 changed files with 796 additions and 114 deletions
|
@ -9,6 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
|
|
||||||
#include "AssignmentClientMonitor.h"
|
#include "AssignmentClientMonitor.h"
|
||||||
|
@ -17,9 +19,21 @@ const char* NUM_FORKS_PARAMETER = "-n";
|
||||||
|
|
||||||
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
||||||
|
|
||||||
|
void signalHandler(int param){
|
||||||
|
// get the qApp and cast it to an AssignmentClientMonitor
|
||||||
|
AssignmentClientMonitor* app = qobject_cast<AssignmentClientMonitor*>(qApp);
|
||||||
|
|
||||||
|
// tell it to stop the child processes and then go down
|
||||||
|
app->stopChildProcesses();
|
||||||
|
app->quit();
|
||||||
|
}
|
||||||
|
|
||||||
AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks) :
|
AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks) :
|
||||||
QCoreApplication(argc, argv)
|
QCoreApplication(argc, argv)
|
||||||
{
|
{
|
||||||
|
// be a signal handler for SIGTERM so we can stop our children when we get it
|
||||||
|
signal(SIGTERM, signalHandler);
|
||||||
|
|
||||||
// start the Logging class with the parent's target name
|
// start the Logging class with the parent's target name
|
||||||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME);
|
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME);
|
||||||
|
|
||||||
|
@ -38,9 +52,29 @@ AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, int num
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AssignmentClientMonitor::stopChildProcesses() {
|
||||||
|
|
||||||
|
QList<QPointer<QProcess> >::Iterator it = _childProcesses.begin();
|
||||||
|
while (it != _childProcesses.end()) {
|
||||||
|
if (!it->isNull()) {
|
||||||
|
qDebug() << "Monitor is terminating child process" << it->data();
|
||||||
|
|
||||||
|
// don't re-spawn this child when it goes down
|
||||||
|
disconnect(it->data(), 0, this, 0);
|
||||||
|
|
||||||
|
it->data()->terminate();
|
||||||
|
it->data()->waitForFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
it = _childProcesses.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AssignmentClientMonitor::spawnChildClient() {
|
void AssignmentClientMonitor::spawnChildClient() {
|
||||||
QProcess *assignmentClient = new QProcess(this);
|
QProcess *assignmentClient = new QProcess(this);
|
||||||
|
|
||||||
|
_childProcesses.append(QPointer<QProcess>(assignmentClient));
|
||||||
|
|
||||||
// make sure that the output from the child process appears in our output
|
// make sure that the output from the child process appears in our output
|
||||||
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
|
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
|
|
||||||
|
@ -55,5 +89,10 @@ void AssignmentClientMonitor::spawnChildClient() {
|
||||||
|
|
||||||
void AssignmentClientMonitor::childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
|
void AssignmentClientMonitor::childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
|
||||||
qDebug("Replacing dead child assignment client with a new one");
|
qDebug("Replacing dead child assignment client with a new one");
|
||||||
|
|
||||||
|
// remove the old process from our list of child processes
|
||||||
|
qDebug() << "need to remove" << QPointer<QProcess>(qobject_cast<QProcess*>(sender()));
|
||||||
|
_childProcesses.removeOne(QPointer<QProcess>(qobject_cast<QProcess*>(sender())));
|
||||||
|
|
||||||
spawnChildClient();
|
spawnChildClient();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#define hifi_AssignmentClientMonitor_h
|
#define hifi_AssignmentClientMonitor_h
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/qpointer.h>
|
||||||
#include <QtCore/QProcess>
|
#include <QtCore/QProcess>
|
||||||
|
|
||||||
#include <Assignment.h>
|
#include <Assignment.h>
|
||||||
|
@ -23,10 +24,13 @@ class AssignmentClientMonitor : public QCoreApplication {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks);
|
AssignmentClientMonitor(int &argc, char **argv, int numAssignmentClientForks);
|
||||||
|
|
||||||
|
void stopChildProcesses();
|
||||||
private slots:
|
private slots:
|
||||||
void childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
void childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||||
private:
|
private:
|
||||||
void spawnChildClient();
|
void spawnChildClient();
|
||||||
|
QList<QPointer<QProcess> > _childProcesses;
|
||||||
|
|
||||||
QStringList _childArguments;
|
QStringList _childArguments;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,11 +14,10 @@ var position = { x: 700, y: 25, z: 725 };
|
||||||
var audioOptions = {
|
var audioOptions = {
|
||||||
position: position,
|
position: position,
|
||||||
volume: 0.4,
|
volume: 0.4,
|
||||||
loop: true,
|
loop: true
|
||||||
stereo: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var sound = SoundCache.getSound(soundURL, audioOptions.isStereo);
|
var sound = SoundCache.getSound(soundURL);
|
||||||
var injector = null;
|
var injector = null;
|
||||||
var count = 100;
|
var count = 100;
|
||||||
|
|
||||||
|
|
125
examples/html/entityList.html
Normal file
125
examples/html/entityList.html
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
<script>
|
||||||
|
var entities = {};
|
||||||
|
var selectedEntities = [];
|
||||||
|
|
||||||
|
function loaded() {
|
||||||
|
elEntityTable = document.getElementById("entity-table");
|
||||||
|
elRefresh = document.getElementById("refresh");
|
||||||
|
|
||||||
|
function onRowClicked(e) {
|
||||||
|
var id = this.dataset.entityId;
|
||||||
|
|
||||||
|
var selection = [this.dataset.entityId];
|
||||||
|
if (e.shiftKey) {
|
||||||
|
selection = selection.concat(selectedEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedEntities = selection;
|
||||||
|
|
||||||
|
entities[id].el.className = 'selected';
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
|
type: "selectionUpdate",
|
||||||
|
focus: false,
|
||||||
|
entityIds: selection,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowDoubleClicked() {
|
||||||
|
var id = this.dataset.entityId;
|
||||||
|
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
|
type: "selectionUpdate",
|
||||||
|
focus: true,
|
||||||
|
entityIds: [this.dataset.entityId],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addEntity(id, type, url) {
|
||||||
|
if (entities[id] === undefined) {
|
||||||
|
var el = document.createElement('tr');
|
||||||
|
el.setAttribute('id', 'entity_' + id);
|
||||||
|
el.innerHTML += "<td>" + type + "</td>";
|
||||||
|
el.innerHTML += "<td>" + url + "</td>";
|
||||||
|
el.dataset.entityId = id;
|
||||||
|
el.onclick = onRowClicked;
|
||||||
|
el.ondblclick = onRowDoubleClicked;
|
||||||
|
elEntityTable.appendChild(el);
|
||||||
|
|
||||||
|
// Add element to local dict
|
||||||
|
entities[id] = {
|
||||||
|
id: id,
|
||||||
|
name: id,
|
||||||
|
el: el,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeEntity(id) {
|
||||||
|
if (entities[id] !== undefined) {
|
||||||
|
elEntityTable.removeChild(entities[id].el);
|
||||||
|
delete entities[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearEntities() {
|
||||||
|
for (id in entities) {
|
||||||
|
elEntityTable.removeChild(entities[id].el);
|
||||||
|
}
|
||||||
|
entities = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
elRefresh.onclick = function() {
|
||||||
|
clearEntities();
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.EventBridge !== undefined) {
|
||||||
|
EventBridge.scriptEventReceived.connect(function(data) {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
|
||||||
|
if (data.type == "selectionUpdate") {
|
||||||
|
selectedEntities = data.selectedIDs;
|
||||||
|
for (var id in entities) {
|
||||||
|
entities[id].el.className = '';
|
||||||
|
}
|
||||||
|
for (var i = 0; i < data.selectedIDs.length; i++) {
|
||||||
|
var id = data.selectedIDs[i];
|
||||||
|
if (id in entities) {
|
||||||
|
var entity = entities[id];
|
||||||
|
entity.el.className = 'selected';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (data.type == "update") {
|
||||||
|
var newEntities = data.entities;
|
||||||
|
for (var i = 0; i < newEntities.length; i++) {
|
||||||
|
var id = newEntities[i].id;
|
||||||
|
addEntity(id, newEntities[i].type, newEntities[i].url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload='loaded();'>
|
||||||
|
<div>
|
||||||
|
<button id="refresh">Refresh</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table id="entity-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th id="entity-type">Type</th>
|
||||||
|
<th id="entity-url">URL</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="entity-table-body">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -214,7 +214,7 @@
|
||||||
elModelSection.style.display = 'block';
|
elModelSection.style.display = 'block';
|
||||||
elModelURL.value = properties.modelURL;
|
elModelURL.value = properties.modelURL;
|
||||||
elModelAnimationURL.value = properties.animationURL;
|
elModelAnimationURL.value = properties.animationURL;
|
||||||
elModelAnimationPlaying.checked = properties.animationPlaying;
|
elModelAnimationPlaying.checked = properties.animationIsPlaying;
|
||||||
elModelAnimationFPS.value = properties.animationFPS;
|
elModelAnimationFPS.value = properties.animationFPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,3 +93,40 @@ input.coord {
|
||||||
width: 6em;
|
width: 6em;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table#entity-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-family: Sans-Serif;
|
||||||
|
font-size: 12px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entity-table tr {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.selected {
|
||||||
|
background-color: #AAA;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entity-table th {
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
border: 0px black solid;
|
||||||
|
text-align: left;
|
||||||
|
word-wrap: nowrap;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entity-table td {
|
||||||
|
border: 0px black solid;
|
||||||
|
word-wrap: nowrap;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
th#entity-type {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th#entity-url {
|
||||||
|
}
|
||||||
|
|
70
examples/libraries/entityList.js
Normal file
70
examples/libraries/entityList.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
EntityListTool = function(opts) {
|
||||||
|
var that = {};
|
||||||
|
|
||||||
|
var url = Script.resolvePath('html/entityList.html');
|
||||||
|
var webView = new WebWindow('Entities', url, 200, 280);
|
||||||
|
|
||||||
|
var visible = false;
|
||||||
|
|
||||||
|
webView.setVisible(visible);
|
||||||
|
|
||||||
|
that.setVisible = function(newVisible) {
|
||||||
|
visible = newVisible;
|
||||||
|
webView.setVisible(visible);
|
||||||
|
};
|
||||||
|
|
||||||
|
selectionManager.addEventListener(function() {
|
||||||
|
var selectedIDs = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||||
|
selectedIDs.push(selectionManager.selections[i].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = {
|
||||||
|
type: 'selectionUpdate',
|
||||||
|
selectedIDs: selectedIDs,
|
||||||
|
};
|
||||||
|
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
||||||
|
});
|
||||||
|
|
||||||
|
webView.eventBridge.webEventReceived.connect(function(data) {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
if (data.type == "selectionUpdate") {
|
||||||
|
var ids = data.entityIds;
|
||||||
|
var entityIDs = [];
|
||||||
|
for (var i = 0; i < ids.length; i++) {
|
||||||
|
var entityID = Entities.getEntityItemID(ids[i]);
|
||||||
|
if (entityID.isKnownID) {
|
||||||
|
entityIDs.push(entityID);
|
||||||
|
} else {
|
||||||
|
print("Tried to select invalid entity: " + ids[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectionManager.setSelections(entityIDs);
|
||||||
|
if (data.focus) {
|
||||||
|
cameraManager.focus(selectionManager.worldPosition,
|
||||||
|
selectionManager.worldDimensions,
|
||||||
|
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||||
|
}
|
||||||
|
} else if (data.type == "refresh") {
|
||||||
|
var entities = [];
|
||||||
|
var ids = Entities.findEntities(MyAvatar.position, 100);
|
||||||
|
for (var i = 0; i < ids.length; i++) {
|
||||||
|
var id = ids[i];
|
||||||
|
var properties = Entities.getEntityProperties(id);
|
||||||
|
entities.push({
|
||||||
|
id: id.id,
|
||||||
|
type: properties.type,
|
||||||
|
url: properties.type == "Model" ? properties.modelURL : "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var data = {
|
||||||
|
type: "update",
|
||||||
|
entities: entities,
|
||||||
|
};
|
||||||
|
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return that;
|
||||||
|
};
|
|
@ -1242,7 +1242,7 @@ SelectionDisplay = (function () {
|
||||||
Quat.getFront(lastCameraOrientation));
|
Quat.getFront(lastCameraOrientation));
|
||||||
|
|
||||||
var vector = Vec3.subtract(newIntersection, lastPlaneIntersection);
|
var vector = Vec3.subtract(newIntersection, lastPlaneIntersection);
|
||||||
lastPlaneIntersection = newIntersection;
|
vector = grid.snapToGrid(vector);
|
||||||
|
|
||||||
// we only care about the Y axis
|
// we only care about the Y axis
|
||||||
vector.x = 0;
|
vector.x = 0;
|
||||||
|
@ -1258,10 +1258,15 @@ SelectionDisplay = (function () {
|
||||||
Vec3.print(" newPosition:", newPosition);
|
Vec3.print(" newPosition:", newPosition);
|
||||||
}
|
}
|
||||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||||
var properties = Entities.getEntityProperties(SelectionManager.selections[i]);
|
var id = SelectionManager.selections[i];
|
||||||
|
var properties = selectionManager.savedProperties[id.id];
|
||||||
|
|
||||||
var original = properties.position;
|
var original = properties.position;
|
||||||
properties.position = Vec3.sum(properties.position, vector);
|
var newPosition = Vec3.sum(properties.position, vector);
|
||||||
Entities.editEntity(SelectionManager.selections[i], properties);
|
|
||||||
|
Entities.editEntity(id, {
|
||||||
|
position: newPosition,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectionManager._update();
|
SelectionManager._update();
|
||||||
|
|
|
@ -39,18 +39,23 @@ var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8};
|
||||||
|
|
||||||
var HELMET_ATTACHMENT_URL = HIFI_PUBLIC_BUCKET + "models/attachments/IronManMaskOnly.fbx"
|
var HELMET_ATTACHMENT_URL = HIFI_PUBLIC_BUCKET + "models/attachments/IronManMaskOnly.fbx"
|
||||||
|
|
||||||
var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.raw")
|
var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.stereo.raw")
|
||||||
var currentDrone = null;
|
var currentDrone = null;
|
||||||
|
|
||||||
var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.raw")
|
var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.stereo.raw")
|
||||||
var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.raw")
|
var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.stereo.raw")
|
||||||
var currentMusak = null;
|
var currentMusakInjector = null;
|
||||||
|
var currentSound = null;
|
||||||
|
|
||||||
|
var inOculusMode = Menu.isOptionChecked("EnableVRMode");
|
||||||
|
|
||||||
function reticlePosition() {
|
function reticlePosition() {
|
||||||
var RETICLE_DISTANCE = 1;
|
var RETICLE_DISTANCE = 1;
|
||||||
return Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), RETICLE_DISTANCE));
|
return Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), RETICLE_DISTANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var MAX_NUM_PANELS = 21;
|
||||||
|
|
||||||
function drawLobby() {
|
function drawLobby() {
|
||||||
if (!panelWall) {
|
if (!panelWall) {
|
||||||
print("Adding overlays for the lobby panel wall and orb shell.");
|
print("Adding overlays for the lobby panel wall and orb shell.");
|
||||||
|
@ -64,7 +69,7 @@ function drawLobby() {
|
||||||
url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Lobby5_PanelsWithFrames.fbx",
|
url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Lobby5_PanelsWithFrames.fbx",
|
||||||
position: Vec3.sum(orbPosition, Vec3.multiplyQbyV(towardsMe, panelsCenterShift)),
|
position: Vec3.sum(orbPosition, Vec3.multiplyQbyV(towardsMe, panelsCenterShift)),
|
||||||
rotation: towardsMe,
|
rotation: towardsMe,
|
||||||
dimensions: panelsDimensions
|
dimensions: panelsDimensions,
|
||||||
};
|
};
|
||||||
|
|
||||||
var orbShellProps = {
|
var orbShellProps = {
|
||||||
|
@ -77,21 +82,23 @@ function drawLobby() {
|
||||||
|
|
||||||
avatarStickPosition = MyAvatar.position;
|
avatarStickPosition = MyAvatar.position;
|
||||||
|
|
||||||
panelWall = Overlays.addOverlay("model", panelWallProps);
|
panelWall = Overlays.addOverlay("model", panelWallProps);
|
||||||
orbShell = Overlays.addOverlay("model", orbShellProps);
|
orbShell = Overlays.addOverlay("model", orbShellProps);
|
||||||
|
|
||||||
// for HMD wearers, create a reticle in center of screen
|
// for HMD wearers, create a reticle in center of screen
|
||||||
var CURSOR_SCALE = 0.025;
|
if (inOculusMode) {
|
||||||
|
var CURSOR_SCALE = 0.025;
|
||||||
reticle = Overlays.addOverlay("billboard", {
|
|
||||||
url: HIFI_PUBLIC_BUCKET + "images/cursor.svg",
|
|
||||||
position: reticlePosition(),
|
|
||||||
ignoreRayIntersection: true,
|
|
||||||
isFacingAvatar: true,
|
|
||||||
alpha: 1.0,
|
|
||||||
scale: CURSOR_SCALE
|
|
||||||
});
|
|
||||||
|
|
||||||
|
reticle = Overlays.addOverlay("billboard", {
|
||||||
|
url: HIFI_PUBLIC_BUCKET + "images/cursor.svg",
|
||||||
|
position: reticlePosition(),
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
isFacingAvatar: true,
|
||||||
|
alpha: 1.0,
|
||||||
|
scale: CURSOR_SCALE
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// add an attachment on this avatar so other people see them in the lobby
|
// add an attachment on this avatar so other people see them in the lobby
|
||||||
MyAvatar.attach(HELMET_ATTACHMENT_URL, "Neck", {x: 0, y: 0, z: 0}, Quat.fromPitchYawRollDegrees(0, 0, 0), 1.15);
|
MyAvatar.attach(HELMET_ATTACHMENT_URL, "Neck", {x: 0, y: 0, z: 0}, Quat.fromPitchYawRollDegrees(0, 0, 0), 1.15);
|
||||||
|
|
||||||
|
@ -125,39 +132,73 @@ function changeLobbyTextures() {
|
||||||
Overlays.editOverlay(panelWall, textureProp);
|
Overlays.editOverlay(panelWall, textureProp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var MUSAK_VOLUME = 0.5;
|
||||||
|
|
||||||
|
function playNextMusak() {
|
||||||
|
if (panelWall) {
|
||||||
|
if (currentSound == latinSound) {
|
||||||
|
if (elevatorSound.downloaded) {
|
||||||
|
currentSound = elevatorSound;
|
||||||
|
}
|
||||||
|
} else if (currentSound == elevatorSound) {
|
||||||
|
if (latinSound.downloaded) {
|
||||||
|
currentSound = latinSound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMusakInjector = Audio.playSound(currentSound, { localOnly: true, volume: MUSAK_VOLUME });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function playRandomMusak() {
|
function playRandomMusak() {
|
||||||
chosenSound = null;
|
currentSound = null;
|
||||||
|
|
||||||
if (latinSound.downloaded && elevatorSound.downloaded) {
|
if (latinSound.downloaded && elevatorSound.downloaded) {
|
||||||
chosenSound = Math.random < 0.5 ? latinSound : elevatorSound;
|
currentSound = Math.random() < 0.5 ? latinSound : elevatorSound;
|
||||||
} else if (latinSound.downloaded) {
|
} else if (latinSound.downloaded) {
|
||||||
chosenSound = latinSound;
|
currentSound = latinSound;
|
||||||
} else if (elevatorSound.downloaded) {
|
} else if (elevatorSound.downloaded) {
|
||||||
chosenSound = elevatorSound;
|
currentSound = elevatorSound;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chosenSound) {
|
if (currentSound) {
|
||||||
currentMusak = Audio.playSound(chosenSound, { stereo: true, localOnly: true })
|
// pick a random number of seconds from 0-10 to offset the musak
|
||||||
|
var secondOffset = Math.random() * 10;
|
||||||
|
currentMusakInjector = Audio.playSound(currentSound, { localOnly: true, secondOffset: secondOffset, volume: MUSAK_VOLUME });
|
||||||
} else {
|
} else {
|
||||||
currentMusak = null;
|
currentMusakInjector = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupLobby() {
|
function cleanupLobby() {
|
||||||
|
|
||||||
|
// for each of the 21 placeholder textures, set them back to default so the cached model doesn't have changed textures
|
||||||
|
var panelTexturesReset = {};
|
||||||
|
panelTexturesReset["textures"] = {};
|
||||||
|
|
||||||
|
for (var j = 0; j < MAX_NUM_PANELS; j++) {
|
||||||
|
panelTexturesReset["textures"]["file" + (j + 1)] = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Texture.jpg";
|
||||||
|
};
|
||||||
|
|
||||||
|
Overlays.editOverlay(panelWall, panelTexturesReset);
|
||||||
|
|
||||||
Overlays.deleteOverlay(panelWall);
|
Overlays.deleteOverlay(panelWall);
|
||||||
Overlays.deleteOverlay(orbShell);
|
Overlays.deleteOverlay(orbShell);
|
||||||
Overlays.deleteOverlay(reticle);
|
|
||||||
|
|
||||||
Audio.stopInjector(currentDrone);
|
if (reticle) {
|
||||||
currentDrone = null;
|
Overlays.deleteOverlay(reticle);
|
||||||
|
}
|
||||||
Audio.stopInjector(currentMusak);
|
|
||||||
currentMusak = null;
|
|
||||||
|
|
||||||
panelWall = false;
|
panelWall = false;
|
||||||
orbShell = false;
|
orbShell = false;
|
||||||
reticle = false;
|
reticle = false;
|
||||||
|
|
||||||
|
Audio.stopInjector(currentDrone);
|
||||||
|
currentDrone = null;
|
||||||
|
|
||||||
|
Audio.stopInjector(currentMusakInjector);
|
||||||
|
currentMusakInjector = null;
|
||||||
|
|
||||||
locations = {};
|
locations = {};
|
||||||
toggleEnvironmentRendering(true);
|
toggleEnvironmentRendering(true);
|
||||||
|
|
||||||
|
@ -218,10 +259,18 @@ function toggleEnvironmentRendering(shouldRender) {
|
||||||
|
|
||||||
function update(deltaTime) {
|
function update(deltaTime) {
|
||||||
maybeCleanupLobby();
|
maybeCleanupLobby();
|
||||||
if (reticle) {
|
if (panelWall) {
|
||||||
Overlays.editOverlay(reticle, {
|
|
||||||
position: reticlePosition()
|
if (reticle) {
|
||||||
});
|
Overlays.editOverlay(reticle, {
|
||||||
|
position: reticlePosition()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the reticle is up then we may need to play the next musak
|
||||||
|
if (!Audio.isInjectorPlaying(currentMusakInjector)) {
|
||||||
|
playNextMusak();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,9 @@ Script.include("libraries/gridTool.js");
|
||||||
var grid = Grid();
|
var grid = Grid();
|
||||||
gridTool = GridTool({ horizontalGrid: grid });
|
gridTool = GridTool({ horizontalGrid: grid });
|
||||||
|
|
||||||
|
Script.include("libraries/entityList.js");
|
||||||
|
var entityListTool = EntityListTool();
|
||||||
|
|
||||||
selectionManager.addEventListener(selectionDisplay.updateHandles);
|
selectionManager.addEventListener(selectionDisplay.updateHandles);
|
||||||
|
|
||||||
var windowDimensions = Controller.getViewportDimensions();
|
var windowDimensions = Controller.getViewportDimensions();
|
||||||
|
@ -283,6 +286,7 @@ var toolBar = (function () {
|
||||||
if (activeButton === toolBar.clicked(clickedOverlay)) {
|
if (activeButton === toolBar.clicked(clickedOverlay)) {
|
||||||
isActive = !isActive;
|
isActive = !isActive;
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
|
entityListTool.setVisible(false);
|
||||||
gridTool.setVisible(false);
|
gridTool.setVisible(false);
|
||||||
grid.setEnabled(false);
|
grid.setEnabled(false);
|
||||||
propertiesTool.setVisible(false);
|
propertiesTool.setVisible(false);
|
||||||
|
@ -290,6 +294,7 @@ var toolBar = (function () {
|
||||||
cameraManager.disable();
|
cameraManager.disable();
|
||||||
} else {
|
} else {
|
||||||
cameraManager.enable();
|
cameraManager.enable();
|
||||||
|
entityListTool.setVisible(true);
|
||||||
gridTool.setVisible(true);
|
gridTool.setVisible(true);
|
||||||
grid.setEnabled(true);
|
grid.setEnabled(true);
|
||||||
propertiesTool.setVisible(true);
|
propertiesTool.setVisible(true);
|
||||||
|
|
|
@ -12,13 +12,12 @@
|
||||||
Script.include("libraries/globals.js");
|
Script.include("libraries/globals.js");
|
||||||
|
|
||||||
var modelURL = HIFI_PUBLIC_BUCKET + "models/entities/radio/Speakers.fbx";
|
var modelURL = HIFI_PUBLIC_BUCKET + "models/entities/radio/Speakers.fbx";
|
||||||
var soundURL = HIFI_PUBLIC_BUCKET + "sounds/FamilyStereo.raw";
|
var soundURL = HIFI_PUBLIC_BUCKET + "sounds/family.stereo.raw";
|
||||||
|
|
||||||
var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
||||||
var audioOptions = {
|
var audioOptions = {
|
||||||
volume: 0.5,
|
volume: 0.5,
|
||||||
loop: true,
|
loop: true
|
||||||
stereo: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var injector = null;
|
var injector = null;
|
||||||
|
|
|
@ -2673,6 +2673,14 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Application::isHMDMode() const {
|
||||||
|
if (OculusManager::isConnected()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
// loadViewFrustum()
|
// loadViewFrustum()
|
||||||
//
|
//
|
||||||
|
|
|
@ -316,6 +316,11 @@ public:
|
||||||
|
|
||||||
void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);
|
void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);
|
||||||
|
|
||||||
|
// the isHMDmode is true whenever we use the interface from an HMD and not a standard flat display
|
||||||
|
// rendering of several elements depend on that
|
||||||
|
// TODO: carry that information on the Camera as a setting
|
||||||
|
bool isHMDMode() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
/// Fired when we're simulating; allows external parties to hook in.
|
/// Fired when we're simulating; allows external parties to hook in.
|
||||||
|
|
|
@ -297,6 +297,8 @@ Menu::Menu() :
|
||||||
avatar, SLOT(updateMotionBehavior()));
|
avatar, SLOT(updateMotionBehavior()));
|
||||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true,
|
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true,
|
||||||
avatar, SLOT(updateMotionBehavior()));
|
avatar, SLOT(updateMotionBehavior()));
|
||||||
|
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false,
|
||||||
|
avatar, SLOT(updateMotionBehavior()));
|
||||||
|
|
||||||
QMenu* collisionsMenu = avatarMenu->addMenu("Collide With...");
|
QMenu* collisionsMenu = avatarMenu->addMenu("Collide With...");
|
||||||
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false,
|
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false,
|
||||||
|
|
|
@ -484,6 +484,7 @@ namespace MenuOption {
|
||||||
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
||||||
const QString SixenseLasers = "Enable Sixense UI Lasers";
|
const QString SixenseLasers = "Enable Sixense UI Lasers";
|
||||||
const QString StandOnNearbyFloors = "Stand on nearby floors";
|
const QString StandOnNearbyFloors = "Stand on nearby floors";
|
||||||
|
const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations";
|
||||||
const QString Stars = "Stars";
|
const QString Stars = "Stars";
|
||||||
const QString Stats = "Stats";
|
const QString Stats = "Stats";
|
||||||
const QString StereoAudio = "Stereo Audio";
|
const QString StereoAudio = "Stereo Audio";
|
||||||
|
|
|
@ -655,7 +655,10 @@ void Avatar::renderDisplayName() {
|
||||||
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
|
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// which viewing mode?
|
||||||
|
bool inHMD = Application::getInstance()->isHMDMode();
|
||||||
|
|
||||||
glDisable(GL_LIGHTING);
|
glDisable(GL_LIGHTING);
|
||||||
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
|
@ -664,17 +667,18 @@ void Avatar::renderDisplayName() {
|
||||||
glTranslatef(textPosition.x, textPosition.y, textPosition.z);
|
glTranslatef(textPosition.x, textPosition.y, textPosition.z);
|
||||||
|
|
||||||
// we need "always facing camera": we must remove the camera rotation from the stack
|
// we need "always facing camera": we must remove the camera rotation from the stack
|
||||||
glm::quat rotation = Application::getInstance()->getCamera()->getRotation();
|
|
||||||
|
|
||||||
glm::vec3 frontAxis(1.0f, 0.0f, 0.0f);
|
glm::vec3 frontAxis(0.0f, 0.0f, 1.0f);
|
||||||
frontAxis = glm::rotate(rotation, frontAxis);
|
if (inHMD) {
|
||||||
frontAxis = glm::normalize(glm::vec3(frontAxis.x, 0.0f, frontAxis.z));
|
glm::vec3 camPosition = Application::getInstance()->getCamera()->getPosition();
|
||||||
|
frontAxis = camPosition - textPosition;
|
||||||
// TODO : test this secodn solution which should be better wfor occulus
|
} else {
|
||||||
//glm::vec3 camPosition = Application::getInstance()->getCamera()->getPosition();
|
glm::quat rotation = Application::getInstance()->getCamera()->getRotation();
|
||||||
//glm::vec3 frontAxis = camPosition - textPosition;
|
frontAxis = glm::rotate(rotation, frontAxis);
|
||||||
//frontAxis = glm::normalize(glm::vec3(frontAxis.z, 0.0f, -frontAxis.x));
|
}
|
||||||
|
|
||||||
|
frontAxis = glm::normalize(glm::vec3(frontAxis.z, 0.0f, -frontAxis.x));
|
||||||
float angle = acos(frontAxis.x) * ((frontAxis.z < 0) ? 1.0f : -1.0f);
|
float angle = acos(frontAxis.x) * ((frontAxis.z < 0) ? 1.0f : -1.0f);
|
||||||
glRotatef(glm::degrees(angle), 0.0f, 1.0f, 0.0f);
|
glRotatef(glm::degrees(angle), 0.0f, 1.0f, 0.0f);
|
||||||
|
|
||||||
|
@ -706,10 +710,16 @@ void Avatar::renderDisplayName() {
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
double textWindowHeight = abs(result1[1] - result0[1]);
|
double textWindowHeight = abs(result1[1] - result0[1]);
|
||||||
float scaleFactor = Application::getInstance()->getRenderResolutionScale() * // Scale compensate for the resolution
|
// need to scale to compensate for the font resolution due to the device
|
||||||
QApplication::desktop()->windowHandle()->devicePixelRatio() * // And the device pixel ratio
|
float scaleFactor = QApplication::desktop()->windowHandle()->devicePixelRatio() *
|
||||||
((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f);
|
((textWindowHeight > EPSILON) ? 1.0f / textWindowHeight : 1.0f);
|
||||||
glScalef(scaleFactor, scaleFactor, 1.0);
|
if (inHMD) {
|
||||||
|
const float HMDMODE_NAME_SCALE = 0.65f;
|
||||||
|
scaleFactor *= HMDMODE_NAME_SCALE;
|
||||||
|
} else {
|
||||||
|
scaleFactor *= Application::getInstance()->getRenderResolutionScale();
|
||||||
|
}
|
||||||
|
glScalef(scaleFactor, scaleFactor, 1.0);
|
||||||
|
|
||||||
glScalef(1.0f, -1.0f, 1.0f); // TextRenderer::draw paints the text upside down in y axis
|
glScalef(1.0f, -1.0f, 1.0f); // TextRenderer::draw paints the text upside down in y axis
|
||||||
|
|
||||||
|
@ -787,7 +797,10 @@ void Avatar::setSkeletonOffset(const glm::vec3& offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 Avatar::getSkeletonPosition() const {
|
glm::vec3 Avatar::getSkeletonPosition() const {
|
||||||
return _position + _skeletonOffset;
|
// The avatar is rotated PI about the yAxis, so we have to correct for it
|
||||||
|
// to get the skeleton offset contribution in the world-frame.
|
||||||
|
const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
return _position + getOrientation() * FLIP * _skeletonOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<glm::quat> Avatar::getJointRotations() const {
|
QVector<glm::quat> Avatar::getJointRotations() const {
|
||||||
|
|
|
@ -204,6 +204,8 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
||||||
_mouth3,
|
_mouth3,
|
||||||
_mouth4,
|
_mouth4,
|
||||||
_blendshapeCoefficients);
|
_blendshapeCoefficients);
|
||||||
|
} else {
|
||||||
|
_saccade = glm::vec3();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMine) {
|
if (!isMine) {
|
||||||
|
|
|
@ -89,6 +89,7 @@ MyAvatar::MyAvatar() :
|
||||||
_billboardValid(false),
|
_billboardValid(false),
|
||||||
_physicsSimulation(),
|
_physicsSimulation(),
|
||||||
_voxelShapeManager(),
|
_voxelShapeManager(),
|
||||||
|
_feetTouchFloor(true),
|
||||||
_isLookingAtLeftEye(true)
|
_isLookingAtLeftEye(true)
|
||||||
{
|
{
|
||||||
ShapeCollider::initDispatchTable();
|
ShapeCollider::initDispatchTable();
|
||||||
|
@ -115,7 +116,7 @@ QByteArray MyAvatar::toByteArray() {
|
||||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
||||||
// fake the avatar position that is sent up to the AvatarMixer
|
// fake the avatar position that is sent up to the AvatarMixer
|
||||||
glm::vec3 oldPosition = _position;
|
glm::vec3 oldPosition = _position;
|
||||||
_position += _skeletonOffset;
|
_position = getSkeletonPosition();
|
||||||
QByteArray array = AvatarData::toByteArray();
|
QByteArray array = AvatarData::toByteArray();
|
||||||
// copy the correct position back
|
// copy the correct position back
|
||||||
_position = oldPosition;
|
_position = oldPosition;
|
||||||
|
@ -155,6 +156,9 @@ void MyAvatar::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
simulate(deltaTime);
|
simulate(deltaTime);
|
||||||
|
if (_feetTouchFloor) {
|
||||||
|
_skeletonModel.updateStandingFoot();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::simulate(float deltaTime) {
|
void MyAvatar::simulate(float deltaTime) {
|
||||||
|
@ -1034,7 +1038,14 @@ void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData)
|
||||||
glm::vec3 MyAvatar::getSkeletonPosition() const {
|
glm::vec3 MyAvatar::getSkeletonPosition() const {
|
||||||
CameraMode mode = Application::getInstance()->getCamera()->getMode();
|
CameraMode mode = Application::getInstance()->getCamera()->getMode();
|
||||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
||||||
return Avatar::getSkeletonPosition();
|
// The avatar is rotated PI about the yAxis, so we have to correct for it
|
||||||
|
// to get the skeleton offset contribution in the world-frame.
|
||||||
|
const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
glm::vec3 skeletonOffset = _skeletonOffset;
|
||||||
|
if (_feetTouchFloor) {
|
||||||
|
skeletonOffset += _skeletonModel.getStandingOffset();
|
||||||
|
}
|
||||||
|
return _position + getOrientation() * FLIP * skeletonOffset;
|
||||||
}
|
}
|
||||||
return Avatar::getPosition();
|
return Avatar::getPosition();
|
||||||
}
|
}
|
||||||
|
@ -1939,6 +1950,7 @@ void MyAvatar::updateMotionBehavior() {
|
||||||
} else {
|
} else {
|
||||||
_motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
|
_motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
|
||||||
}
|
}
|
||||||
|
_feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::onToggleRagdoll() {
|
void MyAvatar::onToggleRagdoll() {
|
||||||
|
|
|
@ -235,6 +235,7 @@ private:
|
||||||
PhysicsSimulation _physicsSimulation;
|
PhysicsSimulation _physicsSimulation;
|
||||||
VoxelShapeManager _voxelShapeManager;
|
VoxelShapeManager _voxelShapeManager;
|
||||||
|
|
||||||
|
bool _feetTouchFloor;
|
||||||
bool _isLookingAtLeftEye;
|
bool _isLookingAtLeftEye;
|
||||||
|
|
||||||
RecorderPointer _recorder;
|
RecorderPointer _recorder;
|
||||||
|
|
|
@ -22,13 +22,22 @@
|
||||||
#include "SkeletonModel.h"
|
#include "SkeletonModel.h"
|
||||||
#include "SkeletonRagdoll.h"
|
#include "SkeletonRagdoll.h"
|
||||||
|
|
||||||
|
enum StandingFootState {
|
||||||
|
LEFT_FOOT,
|
||||||
|
RIGHT_FOOT,
|
||||||
|
NO_FOOT
|
||||||
|
};
|
||||||
|
|
||||||
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
||||||
Model(parent),
|
Model(parent),
|
||||||
_owningAvatar(owningAvatar),
|
_owningAvatar(owningAvatar),
|
||||||
_boundingShape(),
|
_boundingShape(),
|
||||||
_boundingShapeLocalOffset(0.0f),
|
_boundingShapeLocalOffset(0.0f),
|
||||||
_ragdoll(NULL),
|
_ragdoll(NULL),
|
||||||
_defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)) {
|
_defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||||
|
_standingFoot(NO_FOOT),
|
||||||
|
_standingOffset(0.0f),
|
||||||
|
_clampedFootPosition(0.0f) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SkeletonModel::~SkeletonModel() {
|
SkeletonModel::~SkeletonModel() {
|
||||||
|
@ -607,6 +616,62 @@ void SkeletonModel::updateVisibleJointStates() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \return offset of hips after foot animation
|
||||||
|
void SkeletonModel::updateStandingFoot() {
|
||||||
|
glm::vec3 offset(0.0f);
|
||||||
|
int leftFootIndex = _geometry->getFBXGeometry().leftToeJointIndex;
|
||||||
|
int rightFootIndex = _geometry->getFBXGeometry().rightToeJointIndex;
|
||||||
|
|
||||||
|
if (leftFootIndex != -1 && rightFootIndex != -1) {
|
||||||
|
glm::vec3 leftPosition, rightPosition;
|
||||||
|
getJointPosition(leftFootIndex, leftPosition);
|
||||||
|
getJointPosition(rightFootIndex, rightPosition);
|
||||||
|
|
||||||
|
int lowestFoot = (leftPosition.y < rightPosition.y) ? LEFT_FOOT : RIGHT_FOOT;
|
||||||
|
const float MIN_STEP_HEIGHT_THRESHOLD = 0.05f;
|
||||||
|
bool oneFoot = fabsf(leftPosition.y - rightPosition.y) > MIN_STEP_HEIGHT_THRESHOLD;
|
||||||
|
int currentFoot = oneFoot ? lowestFoot : _standingFoot;
|
||||||
|
|
||||||
|
if (_standingFoot == NO_FOOT) {
|
||||||
|
currentFoot = lowestFoot;
|
||||||
|
}
|
||||||
|
if (currentFoot != _standingFoot) {
|
||||||
|
if (_standingFoot == NO_FOOT) {
|
||||||
|
// pick the lowest foot
|
||||||
|
glm::vec3 lowestPosition = (currentFoot == LEFT_FOOT) ? leftPosition : rightPosition;
|
||||||
|
// we ignore zero length positions which can happen for a few frames until skeleton is fully loaded
|
||||||
|
if (glm::length(lowestPosition) > 0.0f) {
|
||||||
|
_standingFoot = currentFoot;
|
||||||
|
_clampedFootPosition = lowestPosition;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// swap feet
|
||||||
|
_standingFoot = currentFoot;
|
||||||
|
glm::vec3 nextPosition = leftPosition;
|
||||||
|
glm::vec3 prevPosition = rightPosition;
|
||||||
|
if (_standingFoot == RIGHT_FOOT) {
|
||||||
|
nextPosition = rightPosition;
|
||||||
|
prevPosition = leftPosition;
|
||||||
|
}
|
||||||
|
glm::vec3 oldOffset = _clampedFootPosition - prevPosition;
|
||||||
|
_clampedFootPosition = oldOffset + nextPosition;
|
||||||
|
offset = _clampedFootPosition - nextPosition;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
glm::vec3 nextPosition = (_standingFoot == LEFT_FOOT) ? leftPosition : rightPosition;
|
||||||
|
offset = _clampedFootPosition - nextPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clamp the offset to not exceed some max distance
|
||||||
|
const float MAX_STEP_OFFSET = 1.0f;
|
||||||
|
float stepDistance = glm::length(offset);
|
||||||
|
if (stepDistance > MAX_STEP_OFFSET) {
|
||||||
|
offset *= (MAX_STEP_OFFSET / stepDistance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_standingOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
SkeletonRagdoll* SkeletonModel::buildRagdoll() {
|
SkeletonRagdoll* SkeletonModel::buildRagdoll() {
|
||||||
if (!_ragdoll) {
|
if (!_ragdoll) {
|
||||||
_ragdoll = new SkeletonRagdoll(this);
|
_ragdoll = new SkeletonRagdoll(this);
|
||||||
|
|
|
@ -101,6 +101,10 @@ public:
|
||||||
/// \return whether or not the head was found.
|
/// \return whether or not the head was found.
|
||||||
glm::vec3 getDefaultEyeModelPosition() const;
|
glm::vec3 getDefaultEyeModelPosition() const;
|
||||||
|
|
||||||
|
/// skeleton offset caused by moving feet
|
||||||
|
void updateStandingFoot();
|
||||||
|
const glm::vec3& getStandingOffset() const { return _standingOffset; }
|
||||||
|
|
||||||
virtual void updateVisibleJointStates();
|
virtual void updateVisibleJointStates();
|
||||||
|
|
||||||
SkeletonRagdoll* buildRagdoll();
|
SkeletonRagdoll* buildRagdoll();
|
||||||
|
@ -154,6 +158,9 @@ private:
|
||||||
SkeletonRagdoll* _ragdoll;
|
SkeletonRagdoll* _ragdoll;
|
||||||
|
|
||||||
glm::vec3 _defaultEyeModelPosition;
|
glm::vec3 _defaultEyeModelPosition;
|
||||||
|
int _standingFoot;
|
||||||
|
glm::vec3 _standingOffset;
|
||||||
|
glm::vec3 _clampedFootPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_SkeletonModel_h
|
#endif // hifi_SkeletonModel_h
|
||||||
|
|
|
@ -78,6 +78,17 @@ float AudioInjector::getLoudness() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioInjector::injectAudio() {
|
void AudioInjector::injectAudio() {
|
||||||
|
|
||||||
|
// check if we need to offset the sound by some number of seconds
|
||||||
|
if (_options.secondOffset > 0.0f) {
|
||||||
|
|
||||||
|
// convert the offset into a number of bytes
|
||||||
|
int byteOffset = (int) floorf(SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f));
|
||||||
|
byteOffset *= sizeof(int16_t);
|
||||||
|
|
||||||
|
_currentSendPosition = byteOffset;
|
||||||
|
}
|
||||||
|
|
||||||
if (_options.localOnly) {
|
if (_options.localOnly) {
|
||||||
injectLocally();
|
injectLocally();
|
||||||
} else {
|
} else {
|
||||||
|
@ -89,10 +100,14 @@ void AudioInjector::injectLocally() {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (_localAudioInterface) {
|
if (_localAudioInterface) {
|
||||||
if (_audioData.size() > 0) {
|
if (_audioData.size() > 0) {
|
||||||
|
|
||||||
_localBuffer = new AudioInjectorLocalBuffer(_audioData, this);
|
_localBuffer = new AudioInjectorLocalBuffer(_audioData, this);
|
||||||
_localBuffer->open(QIODevice::ReadOnly);
|
_localBuffer->open(QIODevice::ReadOnly);
|
||||||
_localBuffer->setShouldLoop(_options.loop);
|
_localBuffer->setShouldLoop(_options.loop);
|
||||||
|
|
||||||
|
// give our current send position to the local buffer
|
||||||
|
_localBuffer->setCurrentOffset(_currentSendPosition);
|
||||||
|
|
||||||
QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector",
|
QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector",
|
||||||
Qt::BlockingQueuedConnection,
|
Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(bool, success),
|
Q_RETURN_ARG(bool, success),
|
||||||
|
@ -100,7 +115,8 @@ void AudioInjector::injectLocally() {
|
||||||
Q_ARG(qreal, _options.volume),
|
Q_ARG(qreal, _options.volume),
|
||||||
Q_ARG(AudioInjector*, this));
|
Q_ARG(AudioInjector*, this));
|
||||||
|
|
||||||
|
// if we're not looping and the buffer tells us it is empty then emit finished
|
||||||
|
connect(_localBuffer, &AudioInjectorLocalBuffer::bufferEmpty, this, &AudioInjector::stop);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qDebug() << "AudioInjector::injectLocally could not output locally via _localAudioInterface";
|
qDebug() << "AudioInjector::injectLocally could not output locally via _localAudioInterface";
|
||||||
|
|
|
@ -47,6 +47,11 @@ qint64 AudioInjectorLocalBuffer::readData(char* data, qint64 maxSize) {
|
||||||
_currentOffset += bytesRead;
|
_currentOffset += bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_shouldLoop && bytesRead == bytesToEnd) {
|
||||||
|
// we hit the end of the buffer, emit a signal
|
||||||
|
emit bufferEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
return bytesRead;
|
return bytesRead;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -26,6 +26,9 @@ public:
|
||||||
|
|
||||||
void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; }
|
void setShouldLoop(bool shouldLoop) { _shouldLoop = shouldLoop; }
|
||||||
|
|
||||||
|
void setCurrentOffset(int currentOffset) { _currentOffset = currentOffset; }
|
||||||
|
signals:
|
||||||
|
void bufferEmpty();
|
||||||
private:
|
private:
|
||||||
|
|
||||||
qint64 recursiveReadFromFront(char* data, qint64 maxSize);
|
qint64 recursiveReadFromFront(char* data, qint64 maxSize);
|
||||||
|
|
|
@ -20,7 +20,8 @@ AudioInjectorOptions::AudioInjectorOptions() :
|
||||||
orientation(glm::vec3(0.0f, 0.0f, 0.0f)),
|
orientation(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||||
stereo(false),
|
stereo(false),
|
||||||
ignorePenumbra(false),
|
ignorePenumbra(false),
|
||||||
localOnly(false)
|
localOnly(false),
|
||||||
|
secondOffset(0.0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,9 +32,9 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje
|
||||||
obj.setProperty("volume", injectorOptions.volume);
|
obj.setProperty("volume", injectorOptions.volume);
|
||||||
obj.setProperty("loop", injectorOptions.loop);
|
obj.setProperty("loop", injectorOptions.loop);
|
||||||
obj.setProperty("orientation", quatToScriptValue(engine, injectorOptions.orientation));
|
obj.setProperty("orientation", quatToScriptValue(engine, injectorOptions.orientation));
|
||||||
obj.setProperty("stereo", injectorOptions.stereo);
|
|
||||||
obj.setProperty("ignorePenumbra", injectorOptions.ignorePenumbra);
|
obj.setProperty("ignorePenumbra", injectorOptions.ignorePenumbra);
|
||||||
obj.setProperty("localOnly", injectorOptions.localOnly);
|
obj.setProperty("localOnly", injectorOptions.localOnly);
|
||||||
|
obj.setProperty("secondOffset", injectorOptions.secondOffset);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,10 +55,6 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt
|
||||||
quatFromScriptValue(object.property("orientation"), injectorOptions.orientation);
|
quatFromScriptValue(object.property("orientation"), injectorOptions.orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object.property("stereo").isValid()) {
|
|
||||||
injectorOptions.stereo = object.property("stereo").toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (object.property("ignorePenumbra").isValid()) {
|
if (object.property("ignorePenumbra").isValid()) {
|
||||||
injectorOptions.ignorePenumbra = object.property("ignorePenumbra").toBool();
|
injectorOptions.ignorePenumbra = object.property("ignorePenumbra").toBool();
|
||||||
}
|
}
|
||||||
|
@ -65,4 +62,8 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt
|
||||||
if (object.property("localOnly").isValid()) {
|
if (object.property("localOnly").isValid()) {
|
||||||
injectorOptions.localOnly = object.property("localOnly").toBool();
|
injectorOptions.localOnly = object.property("localOnly").toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (object.property("secondOffset").isValid()) {
|
||||||
|
injectorOptions.secondOffset = object.property("secondOffset").toNumber();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -27,6 +27,7 @@ public:
|
||||||
bool stereo;
|
bool stereo;
|
||||||
bool ignorePenumbra;
|
bool ignorePenumbra;
|
||||||
bool localOnly;
|
bool localOnly;
|
||||||
|
float secondOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(AudioInjectorOptions);
|
Q_DECLARE_METATYPE(AudioInjectorOptions);
|
||||||
|
|
|
@ -44,7 +44,11 @@ void AudioScriptingInterface::stopAllInjectors() {
|
||||||
|
|
||||||
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
|
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
|
||||||
if (sound) {
|
if (sound) {
|
||||||
AudioInjector* injector = new AudioInjector(sound, injectorOptions);
|
// stereo option isn't set from script, this comes from sound metadata or filename
|
||||||
|
AudioInjectorOptions optionsCopy = injectorOptions;
|
||||||
|
optionsCopy.stereo = sound->isStereo();
|
||||||
|
|
||||||
|
AudioInjector* injector = new AudioInjector(sound, optionsCopy);
|
||||||
injector->setLocalAudioInterface(_localAudioInterface);
|
injector->setLocalAudioInterface(_localAudioInterface);
|
||||||
|
|
||||||
QThread* injectorThread = new QThread();
|
QThread* injectorThread = new QThread();
|
||||||
|
|
|
@ -169,9 +169,16 @@ int InboundAudioStream::parseData(const QByteArray& packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int InboundAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
|
int InboundAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
|
||||||
// mixed audio packets do not have any info between the seq num and the audio data.
|
if (type == PacketTypeSilentAudioFrame) {
|
||||||
numAudioSamples = packetAfterSeqNum.size() / sizeof(int16_t);
|
quint16 numSilentSamples = 0;
|
||||||
return 0;
|
memcpy(&numSilentSamples, packetAfterSeqNum.constData(), sizeof(quint16));
|
||||||
|
numAudioSamples = numSilentSamples;
|
||||||
|
return sizeof(quint16);
|
||||||
|
} else {
|
||||||
|
// mixed audio packets do not have any info between the seq num and the audio data.
|
||||||
|
numAudioSamples = packetAfterSeqNum.size() / sizeof(int16_t);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int numAudioSamples) {
|
int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties, int numAudioSamples) {
|
||||||
|
|
|
@ -64,7 +64,14 @@ void Sound::downloadFinished(QNetworkReply* reply) {
|
||||||
interpretAsWav(rawAudioByteArray, outputAudioByteArray);
|
interpretAsWav(rawAudioByteArray, outputAudioByteArray);
|
||||||
downSample(outputAudioByteArray);
|
downSample(outputAudioByteArray);
|
||||||
} else {
|
} else {
|
||||||
// Process as RAW file
|
// check if this was a stereo raw file
|
||||||
|
// since it's raw the only way for us to know that is if the file was called .stereo.raw
|
||||||
|
if (reply->url().fileName().toLower().endsWith("stereo.raw")) {
|
||||||
|
_isStereo = true;
|
||||||
|
qDebug() << "Processing sound from" << reply->url() << "as stereo audio file.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process as RAW file
|
||||||
downSample(rawAudioByteArray);
|
downSample(rawAudioByteArray);
|
||||||
}
|
}
|
||||||
trimFrames();
|
trimFrames();
|
||||||
|
@ -206,10 +213,12 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou
|
||||||
qDebug() << "Currently not supporting non PCM audio files.";
|
qDebug() << "Currently not supporting non PCM audio files.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (qFromLittleEndian<quint16>(fileHeader.wave.numChannels) != 1) {
|
if (qFromLittleEndian<quint16>(fileHeader.wave.numChannels) == 2) {
|
||||||
qDebug() << "Currently not supporting stereo audio files.";
|
_isStereo = true;
|
||||||
return;
|
} else if (qFromLittleEndian<quint16>(fileHeader.wave.numChannels) > 2) {
|
||||||
|
qDebug() << "Currently not support audio files with more than 2 channels.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qFromLittleEndian<quint16>(fileHeader.wave.bitsPerSample) != 16) {
|
if (qFromLittleEndian<quint16>(fileHeader.wave.bitsPerSample) != 16) {
|
||||||
qDebug() << "Currently not supporting non 16bit audio files.";
|
qDebug() << "Currently not supporting non 16bit audio files.";
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Sound : public Resource {
|
||||||
public:
|
public:
|
||||||
Sound(const QUrl& url, bool isStereo = false);
|
Sound(const QUrl& url, bool isStereo = false);
|
||||||
|
|
||||||
bool isStereo() const { return _isStereo; }
|
bool isStereo() const { return _isStereo; }
|
||||||
bool isReady() const { return _isReady; }
|
bool isReady() const { return _isReady; }
|
||||||
|
|
||||||
const QByteArray& getByteArray() { return _byteArray; }
|
const QByteArray& getByteArray() { return _byteArray; }
|
||||||
|
|
|
@ -90,6 +90,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) {
|
||||||
_lastEditedFromRemoteInRemoteTime = 0;
|
_lastEditedFromRemoteInRemoteTime = 0;
|
||||||
_lastUpdated = 0;
|
_lastUpdated = 0;
|
||||||
_created = 0;
|
_created = 0;
|
||||||
|
_updateFlags = 0;
|
||||||
_changedOnServer = 0;
|
_changedOnServer = 0;
|
||||||
initFromEntityItemID(entityItemID);
|
initFromEntityItemID(entityItemID);
|
||||||
_simulationState = EntityItem::Static;
|
_simulationState = EntityItem::Static;
|
||||||
|
@ -102,6 +103,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert
|
||||||
_lastEditedFromRemoteInRemoteTime = 0;
|
_lastEditedFromRemoteInRemoteTime = 0;
|
||||||
_lastUpdated = 0;
|
_lastUpdated = 0;
|
||||||
_created = properties.getCreated();
|
_created = properties.getCreated();
|
||||||
|
_updateFlags = 0;
|
||||||
_changedOnServer = 0;
|
_changedOnServer = 0;
|
||||||
initFromEntityItemID(entityItemID);
|
initFromEntityItemID(entityItemID);
|
||||||
setProperties(properties, true); // force copy
|
setProperties(properties, true); // force copy
|
||||||
|
@ -465,7 +467,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
dataAt += propertyFlags.getEncodedLength();
|
dataAt += propertyFlags.getEncodedLength();
|
||||||
bytesRead += propertyFlags.getEncodedLength();
|
bytesRead += propertyFlags.getEncodedLength();
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, _position);
|
READ_ENTITY_PROPERTY_SETTER(PROP_POSITION, glm::vec3, updatePosition);
|
||||||
|
|
||||||
// Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS
|
// Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS
|
||||||
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) {
|
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) {
|
||||||
|
@ -484,7 +486,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, _dimensions);
|
READ_ENTITY_PROPERTY_SETTER(PROP_DIMENSIONS, glm::vec3, setDimensions);
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
qDebug() << " readEntityDataFromBuffer() NEW FORMAT... look for PROP_DIMENSIONS";
|
qDebug() << " readEntityDataFromBuffer() NEW FORMAT... look for PROP_DIMENSIONS";
|
||||||
}
|
}
|
||||||
|
@ -494,19 +496,19 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
qDebug() << " readEntityDataFromBuffer() _dimensions:" << getDimensionsInMeters() << " in meters";
|
qDebug() << " readEntityDataFromBuffer() _dimensions:" << getDimensionsInMeters() << " in meters";
|
||||||
}
|
}
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY_QUAT(PROP_ROTATION, _rotation);
|
READ_ENTITY_PROPERTY_QUAT_SETTER(PROP_ROTATION, updateRotation);
|
||||||
READ_ENTITY_PROPERTY(PROP_MASS, float, _mass);
|
READ_ENTITY_PROPERTY_SETTER(PROP_MASS, float, updateMass);
|
||||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, _velocity);
|
READ_ENTITY_PROPERTY_SETTER(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, _gravity);
|
READ_ENTITY_PROPERTY_SETTER(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
|
READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
|
||||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, _lifetime);
|
READ_ENTITY_PROPERTY_SETTER(PROP_LIFETIME, float, updateLifetime);
|
||||||
READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript);
|
READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript);
|
||||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint);
|
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint);
|
||||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, _angularVelocity);
|
READ_ENTITY_PROPERTY_SETTER(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping);
|
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping);
|
||||||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible);
|
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible);
|
||||||
READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, _ignoreForCollisions);
|
READ_ENTITY_PROPERTY_SETTER(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions);
|
||||||
READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, _collisionsWillMove);
|
READ_ENTITY_PROPERTY_SETTER(PROP_COLLISIONS_WILL_MOVE, bool, updateCollisionsWillMove);
|
||||||
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, _locked);
|
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, _locked);
|
||||||
READ_ENTITY_PROPERTY_STRING(PROP_USER_DATA,setUserData);
|
READ_ENTITY_PROPERTY_STRING(PROP_USER_DATA,setUserData);
|
||||||
|
|
||||||
|
@ -731,11 +733,6 @@ bool EntityItem::lifetimeHasExpired() const {
|
||||||
return isMortal() && (getAge() > getLifetime());
|
return isMortal() && (getAge() > getLifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EntityItem::copyChangedProperties(const EntityItem& other) {
|
|
||||||
*this = other;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityItemProperties EntityItem::getProperties() const {
|
EntityItemProperties EntityItem::getProperties() const {
|
||||||
EntityItemProperties properties;
|
EntityItemProperties properties;
|
||||||
properties._id = getID();
|
properties._id = getID();
|
||||||
|
@ -947,4 +944,111 @@ void EntityItem::recalculateCollisionShape() {
|
||||||
_collisionShape.setScale(entityAACube.getScale());
|
_collisionShape.setScale(entityAACube.getScale());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityItem::updatePosition(const glm::vec3& value) {
|
||||||
|
if (_position != value) {
|
||||||
|
_position = value;
|
||||||
|
recalculateCollisionShape();
|
||||||
|
_updateFlags |= EntityItem::UPDATE_POSITION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updatePositionInMeters(const glm::vec3& value) {
|
||||||
|
glm::vec3 position = glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f);
|
||||||
|
if (_position != position) {
|
||||||
|
_position = position;
|
||||||
|
recalculateCollisionShape();
|
||||||
|
_updateFlags |= EntityItem::UPDATE_POSITION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateDimensions(const glm::vec3& value) {
|
||||||
|
if (_dimensions != value) {
|
||||||
|
_dimensions = value;
|
||||||
|
recalculateCollisionShape();
|
||||||
|
_updateFlags |= EntityItem::UPDATE_SHAPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateDimensionsInMeters(const glm::vec3& value) {
|
||||||
|
glm::vec3 dimensions = value / (float) TREE_SCALE;
|
||||||
|
if (_dimensions != dimensions) {
|
||||||
|
_dimensions = dimensions;
|
||||||
|
recalculateCollisionShape();
|
||||||
|
_updateFlags |= EntityItem::UPDATE_SHAPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateRotation(const glm::quat& rotation) {
|
||||||
|
if (_rotation != rotation) {
|
||||||
|
_rotation = rotation;
|
||||||
|
recalculateCollisionShape();
|
||||||
|
_updateFlags |= EntityItem::UPDATE_POSITION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateMass(float value) {
|
||||||
|
if (_mass != value) {
|
||||||
|
_mass = value;
|
||||||
|
_updateFlags |= EntityItem::UPDATE_MASS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateVelocity(const glm::vec3& value) {
|
||||||
|
if (_velocity != value) {
|
||||||
|
_velocity = value;
|
||||||
|
_updateFlags |= EntityItem::UPDATE_VELOCITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateVelocityInMeters(const glm::vec3& value) {
|
||||||
|
glm::vec3 velocity = value / (float) TREE_SCALE;
|
||||||
|
if (_velocity != velocity) {
|
||||||
|
_velocity = velocity;
|
||||||
|
_updateFlags |= EntityItem::UPDATE_VELOCITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateGravity(const glm::vec3& value) {
|
||||||
|
if (_gravity != value) {
|
||||||
|
_gravity = value;
|
||||||
|
_updateFlags |= EntityItem::UPDATE_VELOCITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateGravityInMeters(const glm::vec3& value) {
|
||||||
|
glm::vec3 gravity = value / (float) TREE_SCALE;
|
||||||
|
if (_gravity != gravity) {
|
||||||
|
_gravity = gravity;
|
||||||
|
_updateFlags |= EntityItem::UPDATE_VELOCITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateAngularVelocity(const glm::vec3& value) {
|
||||||
|
if (_angularVelocity != value) {
|
||||||
|
_angularVelocity = value;
|
||||||
|
_updateFlags |= EntityItem::UPDATE_VELOCITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateIgnoreForCollisions(bool value) {
|
||||||
|
if (_ignoreForCollisions != value) {
|
||||||
|
_ignoreForCollisions = value;
|
||||||
|
_updateFlags |= EntityItem::UPDATE_COLLISION_GROUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateCollisionsWillMove(bool value) {
|
||||||
|
if (_collisionsWillMove != value) {
|
||||||
|
_collisionsWillMove = value;
|
||||||
|
_updateFlags |= EntityItem::UPDATE_MOTION_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateLifetime(float value) {
|
||||||
|
if (_lifetime != value) {
|
||||||
|
_lifetime = value;
|
||||||
|
_updateFlags |= EntityItem::UPDATE_LIFETIME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,13 +35,23 @@ class EntityTreeElementExtraEncodeData;
|
||||||
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
|
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
|
||||||
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { };
|
#define ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() { };
|
||||||
|
|
||||||
|
|
||||||
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
|
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
|
||||||
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
|
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
|
||||||
/// one directly, instead you must only construct one of it's derived classes with additional features.
|
/// one directly, instead you must only construct one of it's derived classes with additional features.
|
||||||
class EntityItem {
|
class EntityItem {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum EntityUpdateFlags {
|
||||||
|
UPDATE_POSITION = 0x0001,
|
||||||
|
UPDATE_VELOCITY = 0x0002,
|
||||||
|
UPDATE_MASS = 0x0004,
|
||||||
|
UPDATE_COLLISION_GROUP = 0x0008,
|
||||||
|
UPDATE_MOTION_TYPE = 0x0010,
|
||||||
|
UPDATE_SHAPE = 0x0020,
|
||||||
|
UPDATE_LIFETIME = 0x0040
|
||||||
|
//UPDATE_APPEARANCE = 0x8000,
|
||||||
|
};
|
||||||
|
|
||||||
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
|
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
|
||||||
|
|
||||||
EntityItem(const EntityItemID& entityItemID);
|
EntityItem(const EntityItemID& entityItemID);
|
||||||
|
@ -125,9 +135,6 @@ public:
|
||||||
|
|
||||||
virtual void debugDump() const;
|
virtual void debugDump() const;
|
||||||
|
|
||||||
// similar to assignment/copy, but it handles keeping lifetime accurate
|
|
||||||
void copyChangedProperties(const EntityItem& other);
|
|
||||||
|
|
||||||
// attributes applicable to all entity types
|
// attributes applicable to all entity types
|
||||||
EntityTypes::EntityType getType() const { return _type; }
|
EntityTypes::EntityType getType() const { return _type; }
|
||||||
const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0)
|
const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0)
|
||||||
|
@ -266,6 +273,30 @@ public:
|
||||||
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
|
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
|
||||||
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
|
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
|
||||||
|
|
||||||
|
// updateFoo() methods to be used when changes need to be accumulated in the _updateFlags
|
||||||
|
void updatePosition(const glm::vec3& value);
|
||||||
|
void updatePositionInMeters(const glm::vec3& value);
|
||||||
|
void updateDimensions(const glm::vec3& value);
|
||||||
|
void updateDimensionsInMeters(const glm::vec3& value);
|
||||||
|
void updateRotation(const glm::quat& rotation);
|
||||||
|
void updateMass(float value);
|
||||||
|
void updateVelocity(const glm::vec3& value);
|
||||||
|
void updateVelocityInMeters(const glm::vec3& value);
|
||||||
|
void updateGravity(const glm::vec3& value);
|
||||||
|
void updateGravityInMeters(const glm::vec3& value);
|
||||||
|
void updateAngularVelocity(const glm::vec3& value);
|
||||||
|
void updateIgnoreForCollisions(bool value);
|
||||||
|
void updateCollisionsWillMove(bool value);
|
||||||
|
void updateLifetime(float value);
|
||||||
|
|
||||||
|
uint32_t getUpdateFlags() const { return _updateFlags; }
|
||||||
|
void clearUpdateFlags() { _updateFlags = 0; }
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
EntityMotionState* getMotionState() const { return _motionState; }
|
||||||
|
virtual EntityMotionState* createMotionState() { return NULL; }
|
||||||
|
void destroyMotionState();
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
SimulationState getSimulationState() const { return _simulationState; }
|
SimulationState getSimulationState() const { return _simulationState; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -314,6 +345,10 @@ protected:
|
||||||
|
|
||||||
AACubeShape _collisionShape;
|
AACubeShape _collisionShape;
|
||||||
SimulationState _simulationState; // only set by EntityTree
|
SimulationState _simulationState; // only set by EntityTree
|
||||||
|
|
||||||
|
// UpdateFlags are set whenever a property changes that requires the change to be communicated to other
|
||||||
|
// data structures. It is the responsibility of the EntityTree to relay changes entity and clear flags.
|
||||||
|
uint32_t _updateFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,17 @@
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define READ_ENTITY_PROPERTY_QUAT_SETTER(P,M) \
|
||||||
|
if (propertyFlags.getHasProperty(P)) { \
|
||||||
|
glm::quat fromBuffer; \
|
||||||
|
int bytes = unpackOrientationQuatFromBytes(dataAt, fromBuffer); \
|
||||||
|
dataAt += bytes; \
|
||||||
|
bytesRead += bytes; \
|
||||||
|
if (overwriteLocalData) { \
|
||||||
|
M(fromBuffer); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
#define READ_ENTITY_PROPERTY_STRING(P,O) \
|
#define READ_ENTITY_PROPERTY_STRING(P,O) \
|
||||||
if (propertyFlags.getHasProperty(P)) { \
|
if (propertyFlags.getHasProperty(P)) { \
|
||||||
uint16_t length; \
|
uint16_t length; \
|
||||||
|
|
|
@ -44,6 +44,20 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EntityItemID EntityScriptingInterface::getEntityItemID(const QString& uuid) {
|
||||||
|
EntityItemID entityID = EntityItemID(QUuid(uuid), UNKNOWN_ENTITY_TOKEN, false);
|
||||||
|
|
||||||
|
_entityTree->lockForRead();
|
||||||
|
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(entityID));
|
||||||
|
_entityTree->unlock();
|
||||||
|
|
||||||
|
if (entity) {
|
||||||
|
return entity->getEntityItemID();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entityID;
|
||||||
|
}
|
||||||
|
|
||||||
EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) {
|
EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) {
|
||||||
EntityItemID actualID = entityID;
|
EntityItemID actualID = entityID;
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,9 @@ public slots:
|
||||||
/// adds a model with the specific properties
|
/// adds a model with the specific properties
|
||||||
Q_INVOKABLE EntityItemID addEntity(const EntityItemProperties& properties);
|
Q_INVOKABLE EntityItemID addEntity(const EntityItemProperties& properties);
|
||||||
|
|
||||||
|
// Get EntityItemID from uuid string
|
||||||
|
Q_INVOKABLE EntityItemID getEntityItemID(const QString& entityID);
|
||||||
|
|
||||||
/// identify a recently created model to determine its true ID
|
/// identify a recently created model to determine its true ID
|
||||||
Q_INVOKABLE EntityItemID identifyEntity(EntityItemID entityID);
|
Q_INVOKABLE EntityItemID identifyEntity(EntityItemID entityID);
|
||||||
|
|
||||||
|
|
|
@ -657,12 +657,13 @@ void EntityTree::updateChangedEntities(quint64 now, QSet<EntityItemID>& entities
|
||||||
foreach (EntityItem* thisEntity, _changedEntities) {
|
foreach (EntityItem* thisEntity, _changedEntities) {
|
||||||
// check to see if the lifetime has expired, for immortal entities this is always false
|
// check to see if the lifetime has expired, for immortal entities this is always false
|
||||||
if (thisEntity->lifetimeHasExpired()) {
|
if (thisEntity->lifetimeHasExpired()) {
|
||||||
qDebug() << "Lifetime has expired for thisEntity:" << thisEntity->getEntityItemID();
|
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
|
||||||
entitiesToDelete << thisEntity->getEntityItemID();
|
entitiesToDelete << thisEntity->getEntityItemID();
|
||||||
clearEntityState(thisEntity);
|
clearEntityState(thisEntity);
|
||||||
} else {
|
} else {
|
||||||
updateEntityState(thisEntity);
|
updateEntityState(thisEntity);
|
||||||
}
|
}
|
||||||
|
thisEntity->clearUpdateFlags();
|
||||||
}
|
}
|
||||||
_changedEntities.clear();
|
_changedEntities.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -739,8 +739,9 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
||||||
EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID);
|
EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID);
|
||||||
|
|
||||||
bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
|
bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
|
||||||
// TODO: Andrew to only set changed if something has actually changed
|
if (entityItem->getUpdateFlags()) {
|
||||||
_myTree->entityChanged(entityItem);
|
_myTree->entityChanged(entityItem);
|
||||||
|
}
|
||||||
bool bestFitAfter = bestFitEntityBounds(entityItem);
|
bool bestFitAfter = bestFitEntityBounds(entityItem);
|
||||||
|
|
||||||
if (bestFitBefore != bestFitAfter) {
|
if (bestFitBefore != bestFitAfter) {
|
||||||
|
|
|
@ -47,6 +47,13 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
|
||||||
// the getMaximumAACube is the relaxed form.
|
// the getMaximumAACube is the relaxed form.
|
||||||
_oldEntityCube = _existingEntity->getMaximumAACube();
|
_oldEntityCube = _existingEntity->getMaximumAACube();
|
||||||
_oldEntityBox = _oldEntityCube.clamp(0.0f, 1.0f); // clamp to domain bounds
|
_oldEntityBox = _oldEntityCube.clamp(0.0f, 1.0f); // clamp to domain bounds
|
||||||
|
|
||||||
|
// If the old properties doesn't contain the properties required to calculate a bounding box,
|
||||||
|
// get them from the existing entity. Registration point is required to correctly calculate
|
||||||
|
// the bounding box.
|
||||||
|
if (!_properties.registrationPointChanged()) {
|
||||||
|
_properties.setRegistrationPoint(_existingEntity->getRegistrationPoint());
|
||||||
|
}
|
||||||
|
|
||||||
// If the new properties has position OR dimension changes, but not both, we need to
|
// If the new properties has position OR dimension changes, but not both, we need to
|
||||||
// get the old property value and set it in our properties in order for our bounds
|
// get the old property value and set it in our properties in order for our bounds
|
||||||
|
|
|
@ -1067,6 +1067,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
QString jointHeadID;
|
QString jointHeadID;
|
||||||
QString jointLeftHandID;
|
QString jointLeftHandID;
|
||||||
QString jointRightHandID;
|
QString jointRightHandID;
|
||||||
|
QString jointLeftToeID;
|
||||||
|
QString jointRightToeID;
|
||||||
|
|
||||||
QVector<QString> humanIKJointNames;
|
QVector<QString> humanIKJointNames;
|
||||||
for (int i = 0;; i++) {
|
for (int i = 0;; i++) {
|
||||||
|
@ -1166,11 +1168,17 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
} else if (name == jointHeadName) {
|
} else if (name == jointHeadName) {
|
||||||
jointHeadID = getID(object.properties);
|
jointHeadID = getID(object.properties);
|
||||||
|
|
||||||
} else if (name == jointLeftHandName) {
|
} else if (name == jointLeftHandName || name == "LeftHand" || name == "joint_L_hand") {
|
||||||
jointLeftHandID = getID(object.properties);
|
jointLeftHandID = getID(object.properties);
|
||||||
|
|
||||||
} else if (name == jointRightHandName) {
|
} else if (name == jointRightHandName || name == "RightHand" || name == "joint_R_hand") {
|
||||||
jointRightHandID = getID(object.properties);
|
jointRightHandID = getID(object.properties);
|
||||||
|
|
||||||
|
} else if (name == "LeftToe" || name == "joint_L_toe" || name == "LeftToe_End") {
|
||||||
|
jointLeftToeID = getID(object.properties);
|
||||||
|
|
||||||
|
} else if (name == "RightToe" || name == "joint_R_toe" || name == "RightToe_End") {
|
||||||
|
jointRightToeID = getID(object.properties);
|
||||||
}
|
}
|
||||||
int humanIKJointIndex = humanIKJointNames.indexOf(name);
|
int humanIKJointIndex = humanIKJointNames.indexOf(name);
|
||||||
if (humanIKJointIndex != -1) {
|
if (humanIKJointIndex != -1) {
|
||||||
|
@ -1595,6 +1603,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
||||||
geometry.headJointIndex = modelIDs.indexOf(jointHeadID);
|
geometry.headJointIndex = modelIDs.indexOf(jointHeadID);
|
||||||
geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID);
|
geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID);
|
||||||
geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID);
|
geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID);
|
||||||
|
geometry.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID);
|
||||||
|
geometry.rightToeJointIndex = modelIDs.indexOf(jointRightToeID);
|
||||||
|
|
||||||
foreach (const QString& id, humanIKJointIDs) {
|
foreach (const QString& id, humanIKJointIDs) {
|
||||||
geometry.humanIKJointIndices.append(modelIDs.indexOf(id));
|
geometry.humanIKJointIndices.append(modelIDs.indexOf(id));
|
||||||
|
|
|
@ -201,6 +201,8 @@ public:
|
||||||
int headJointIndex;
|
int headJointIndex;
|
||||||
int leftHandJointIndex;
|
int leftHandJointIndex;
|
||||||
int rightHandJointIndex;
|
int rightHandJointIndex;
|
||||||
|
int leftToeJointIndex;
|
||||||
|
int rightToeJointIndex;
|
||||||
|
|
||||||
QVector<int> humanIKJointIndices;
|
QVector<int> humanIKJointIndices;
|
||||||
|
|
||||||
|
|
|
@ -21,57 +21,57 @@
|
||||||
// NOTE: if adding a new packet type, you can replace one marked usable or add at the end
|
// NOTE: if adding a new packet type, you can replace one marked usable or add at the end
|
||||||
// NOTE: if you want the name of the packet type to be available for debugging or logging, update nameForPacketType() as well
|
// NOTE: if you want the name of the packet type to be available for debugging or logging, update nameForPacketType() as well
|
||||||
enum PacketType {
|
enum PacketType {
|
||||||
PacketTypeUnknown,
|
PacketTypeUnknown, // 0
|
||||||
PacketTypeStunResponse,
|
PacketTypeStunResponse,
|
||||||
PacketTypeDomainList,
|
PacketTypeDomainList,
|
||||||
PacketTypePing,
|
PacketTypePing,
|
||||||
PacketTypePingReply,
|
PacketTypePingReply,
|
||||||
PacketTypeKillAvatar,
|
PacketTypeKillAvatar, // 5
|
||||||
PacketTypeAvatarData,
|
PacketTypeAvatarData,
|
||||||
PacketTypeInjectAudio,
|
PacketTypeInjectAudio,
|
||||||
PacketTypeMixedAudio,
|
PacketTypeMixedAudio,
|
||||||
PacketTypeMicrophoneAudioNoEcho,
|
PacketTypeMicrophoneAudioNoEcho,
|
||||||
PacketTypeMicrophoneAudioWithEcho,
|
PacketTypeMicrophoneAudioWithEcho, // 10
|
||||||
PacketTypeBulkAvatarData,
|
PacketTypeBulkAvatarData,
|
||||||
PacketTypeSilentAudioFrame,
|
PacketTypeSilentAudioFrame,
|
||||||
PacketTypeEnvironmentData,
|
PacketTypeEnvironmentData,
|
||||||
PacketTypeDomainListRequest,
|
PacketTypeDomainListRequest,
|
||||||
PacketTypeRequestAssignment,
|
PacketTypeRequestAssignment, // 15
|
||||||
PacketTypeCreateAssignment,
|
PacketTypeCreateAssignment,
|
||||||
PacketTypeDomainConnectionDenied,
|
PacketTypeDomainConnectionDenied,
|
||||||
PacketTypeMuteEnvironment,
|
PacketTypeMuteEnvironment,
|
||||||
PacketTypeAudioStreamStats,
|
PacketTypeAudioStreamStats,
|
||||||
PacketTypeDataServerConfirm,
|
PacketTypeDataServerConfirm, // 20
|
||||||
PacketTypeVoxelQuery,
|
PacketTypeVoxelQuery,
|
||||||
PacketTypeVoxelData,
|
PacketTypeVoxelData,
|
||||||
PacketTypeVoxelSet,
|
PacketTypeVoxelSet,
|
||||||
PacketTypeVoxelSetDestructive,
|
PacketTypeVoxelSetDestructive,
|
||||||
PacketTypeVoxelErase,
|
PacketTypeVoxelErase, // 25
|
||||||
PacketTypeOctreeStats, // 26
|
PacketTypeOctreeStats,
|
||||||
PacketTypeJurisdiction,
|
PacketTypeJurisdiction,
|
||||||
PacketTypeJurisdictionRequest,
|
PacketTypeJurisdictionRequest,
|
||||||
UNUSED_1,
|
UNUSED_1,
|
||||||
UNUSED_2,
|
UNUSED_2, // 30
|
||||||
UNUSED_3,
|
UNUSED_3,
|
||||||
UNUSED_4,
|
UNUSED_4,
|
||||||
PacketTypeNoisyMute,
|
PacketTypeNoisyMute,
|
||||||
PacketTypeMetavoxelData,
|
PacketTypeMetavoxelData,
|
||||||
PacketTypeAvatarIdentity,
|
PacketTypeAvatarIdentity, // 35
|
||||||
PacketTypeAvatarBillboard,
|
PacketTypeAvatarBillboard,
|
||||||
PacketTypeDomainConnectRequest,
|
PacketTypeDomainConnectRequest,
|
||||||
PacketTypeDomainServerRequireDTLS,
|
PacketTypeDomainServerRequireDTLS,
|
||||||
PacketTypeNodeJsonStats,
|
PacketTypeNodeJsonStats,
|
||||||
PacketTypeEntityQuery,
|
PacketTypeEntityQuery, // 40
|
||||||
PacketTypeEntityData, // 41
|
PacketTypeEntityData,
|
||||||
PacketTypeEntityAddOrEdit,
|
PacketTypeEntityAddOrEdit,
|
||||||
PacketTypeEntityErase,
|
PacketTypeEntityErase,
|
||||||
PacketTypeEntityAddResponse,
|
PacketTypeEntityAddResponse,
|
||||||
PacketTypeOctreeDataNack, // 45
|
PacketTypeOctreeDataNack, // 45
|
||||||
PacketTypeVoxelEditNack,
|
PacketTypeVoxelEditNack,
|
||||||
PacketTypeAudioEnvironment,
|
PacketTypeAudioEnvironment,
|
||||||
PacketTypeEntityEditNack, // 48
|
PacketTypeEntityEditNack,
|
||||||
PacketTypeSignedTransactionPayment,
|
PacketTypeSignedTransactionPayment,
|
||||||
PacketTypeIceServerHeartbeat,
|
PacketTypeIceServerHeartbeat, // 50
|
||||||
PacketTypeIceServerHeartbeatResponse,
|
PacketTypeIceServerHeartbeatResponse,
|
||||||
PacketTypeUnverifiedPing,
|
PacketTypeUnverifiedPing,
|
||||||
PacketTypeUnverifiedPingReply
|
PacketTypeUnverifiedPingReply
|
||||||
|
|
Loading…
Reference in a new issue