mirror of
https://github.com/overte-org/overte.git
synced 2025-04-16 13:56:24 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into domain-wizard
This commit is contained in:
commit
e75a2d355e
49 changed files with 1003 additions and 134 deletions
|
@ -40,6 +40,7 @@ var timerOffset;
|
|||
setupToolBar();
|
||||
|
||||
var timer = null;
|
||||
var slider = null;
|
||||
setupTimer();
|
||||
|
||||
var watchStop = false;
|
||||
|
@ -115,6 +116,30 @@ function setupTimer() {
|
|||
alpha: 1.0,
|
||||
visible: true
|
||||
});
|
||||
|
||||
slider = { x: 0, y: 0,
|
||||
w: 200, h: 20,
|
||||
pos: 0.0, // 0.0 <= pos <= 1.0
|
||||
};
|
||||
slider.background = Overlays.addOverlay("text", {
|
||||
text: "",
|
||||
backgroundColor: { red: 128, green: 128, blue: 128 },
|
||||
x: slider.x, y: slider.y,
|
||||
width: slider.w,
|
||||
height: slider.h,
|
||||
alpha: 1.0,
|
||||
visible: true
|
||||
});
|
||||
slider.foreground = Overlays.addOverlay("text", {
|
||||
text: "",
|
||||
backgroundColor: { red: 200, green: 200, blue: 200 },
|
||||
x: slider.x, y: slider.y,
|
||||
width: slider.pos * slider.w,
|
||||
height: slider.h,
|
||||
alpha: 1.0,
|
||||
visible: true
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function updateTimer() {
|
||||
|
@ -131,6 +156,16 @@ function updateTimer() {
|
|||
text: text
|
||||
})
|
||||
toolBar.changeSpacing(text.length * 8 + ((MyAvatar.isRecording()) ? 15 : 0), spacing);
|
||||
|
||||
if (MyAvatar.isRecording()) {
|
||||
slider.pos = 1.0;
|
||||
} else if (MyAvatar.playerLength() > 0) {
|
||||
slider.pos = MyAvatar.playerElapsed() / MyAvatar.playerLength();
|
||||
}
|
||||
|
||||
Overlays.editOverlay(slider.foreground, {
|
||||
width: slider.pos * slider.w
|
||||
});
|
||||
}
|
||||
|
||||
function formatTime(time) {
|
||||
|
@ -163,7 +198,19 @@ function moveUI() {
|
|||
Overlays.editOverlay(timer, {
|
||||
x: relative.x + timerOffset - ToolBar.SPACING,
|
||||
y: windowDimensions.y - relative.y - ToolBar.SPACING
|
||||
});
|
||||
});
|
||||
|
||||
slider.x = relative.x - ToolBar.SPACING;
|
||||
slider.y = windowDimensions.y - relative.y - slider.h - ToolBar.SPACING;
|
||||
|
||||
Overlays.editOverlay(slider.background, {
|
||||
x: slider.x,
|
||||
y: slider.y,
|
||||
});
|
||||
Overlays.editOverlay(slider.foreground, {
|
||||
x: slider.x,
|
||||
y: slider.y,
|
||||
});
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
|
@ -188,7 +235,7 @@ function mousePressEvent(event) {
|
|||
}
|
||||
} else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) {
|
||||
if (MyAvatar.isPlaying()) {
|
||||
MyAvatar.stopPlaying();
|
||||
MyAvatar.pausePlayer();
|
||||
toolBar.setAlpha(ALPHA_ON, recordIcon);
|
||||
toolBar.setAlpha(ALPHA_ON, saveIcon);
|
||||
toolBar.setAlpha(ALPHA_ON, loadIcon);
|
||||
|
@ -203,7 +250,7 @@ function mousePressEvent(event) {
|
|||
}
|
||||
} else if (playLoopIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) {
|
||||
if (MyAvatar.isPlaying()) {
|
||||
MyAvatar.stopPlaying();
|
||||
MyAvatar.pausePlayer();
|
||||
toolBar.setAlpha(ALPHA_ON, recordIcon);
|
||||
toolBar.setAlpha(ALPHA_ON, saveIcon);
|
||||
toolBar.setAlpha(ALPHA_ON, loadIcon);
|
||||
|
@ -234,10 +281,30 @@ function mousePressEvent(event) {
|
|||
toolBar.setAlpha(ALPHA_ON, saveIcon);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
} else if (MyAvatar.playerLength() > 0 &&
|
||||
slider.x < event.x && event.x < slider.x + slider.w &&
|
||||
slider.y < event.y && event.y < slider.y + slider.h) {
|
||||
isSliding = true;
|
||||
slider.pos = (event.x - slider.x) / slider.w;
|
||||
MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength());
|
||||
}
|
||||
}
|
||||
var isSliding = false;
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (isSliding) {
|
||||
slider.pos = (event.x - slider.x) / slider.w;
|
||||
if (slider.pos < 0.0 || slider.pos > 1.0) {
|
||||
MyAvatar.stopPlaying();
|
||||
slider.pos = 0.0;
|
||||
}
|
||||
MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength());
|
||||
}
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
isSliding = false;
|
||||
}
|
||||
|
||||
function update() {
|
||||
var newDimensions = Controller.getViewportDimensions();
|
||||
|
@ -264,11 +331,15 @@ function scriptEnding() {
|
|||
if (MyAvatar.isPlaying()) {
|
||||
MyAvatar.stopPlaying();
|
||||
}
|
||||
toolBar.cleanup();
|
||||
Overlays.deleteOverlay(timer);
|
||||
toolBar.cleanup();
|
||||
Overlays.deleteOverlay(timer);
|
||||
Overlays.deleteOverlay(slider.background);
|
||||
Overlays.deleteOverlay(slider.foreground);
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
|
|
@ -2449,6 +2449,7 @@ function Tooltip() {
|
|||
text += "Lifetime: " + properties.lifetime + "\n"
|
||||
}
|
||||
text += "Age: " + properties.ageAsText + "\n"
|
||||
text += "Mass: " + properties.mass + "\n"
|
||||
text += "Script: " + properties.script + "\n"
|
||||
|
||||
|
||||
|
@ -2922,6 +2923,15 @@ function handeMenuEvent(menuItem) {
|
|||
array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Collisions:", type: "header" });
|
||||
index++;
|
||||
array.push({ label: "Mass:", value: properties.mass.toFixed(decimals) });
|
||||
index++;
|
||||
array.push({ label: "Ignore for Collisions:", value: properties.ignoreForCollisions });
|
||||
index++;
|
||||
array.push({ label: "Collisions Will Move:", value: properties.collisionsWillMove });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
|
||||
index++;
|
||||
|
||||
|
@ -3061,6 +3071,12 @@ Window.nonBlockingFormClosed.connect(function() {
|
|||
properties.gravity.x = array[index++].value;
|
||||
properties.gravity.y = array[index++].value;
|
||||
properties.gravity.z = array[index++].value;
|
||||
|
||||
index++; // skip header
|
||||
properties.mass = array[index++].value;
|
||||
properties.ignoreForCollisions = array[index++].value;
|
||||
properties.collisionsWillMove = array[index++].value;
|
||||
|
||||
properties.lifetime = array[index++].value;
|
||||
properties.visible = array[index++].value;
|
||||
|
||||
|
|
|
@ -1127,8 +1127,6 @@ function keyPressEvent(event) {
|
|||
} else if (event.text == "z") {
|
||||
undoSound.playRandom();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
trackKeyPressEvent(event); // used by preview support
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
//
|
||||
|
||||
|
||||
var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers2Finished.fbx";
|
||||
var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers.fbx";
|
||||
var soundURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/FamilyStereo.raw";
|
||||
|
||||
var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
||||
|
@ -20,7 +20,7 @@ audioOptions.loop = true;
|
|||
audioOptions.isStereo = true;
|
||||
var injector = null;
|
||||
|
||||
var sound = new Sound(soundURL);
|
||||
var sound = new Sound(soundURL, audioOptions.isStereo);
|
||||
|
||||
var entity = null;
|
||||
var properties = null;
|
||||
|
@ -31,14 +31,16 @@ function update() {
|
|||
print("Sound file downloaded");
|
||||
var position = Vec3.sum(MyAvatar.position,
|
||||
Vec3.multiplyQbyV(MyAvatar.orientation,
|
||||
{ x: 0, y: 0.3, z: -1 }));
|
||||
{ x: 0, y: -0.3, z: -2 }));
|
||||
var rotation = Quat.multiply(MyAvatar.orientation,
|
||||
Quat.fromPitchYawRollDegrees(0, -90, 0));
|
||||
entity = Entities.addEntity({
|
||||
type: "Model",
|
||||
position: position,
|
||||
rotation: rotation,
|
||||
dimensions: { x: 0.5, y: 0.5, z: 0.5 },
|
||||
dimensions: { x: 0.391,
|
||||
y: 1.000,
|
||||
z: 1.701 },
|
||||
modelURL: modelURL
|
||||
});
|
||||
properties = Entities.getEntityProperties(entity);
|
||||
|
|
18
interface/resources/images/address-bar-submit-active.svg
Normal file
18
interface/resources/images/address-bar-submit-active.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 49 49" enable-background="new 0 0 49 49" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#333" d="M49,46c0,1.7-1.3,3-3,3H3c-1.7,0-3-1.3-3-3V3c0-1.7,1.3-3,3-3h43c1.7,0,3,1.3,3,3V46z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g id="Your_Icon_11_">
|
||||
<g>
|
||||
<polygon fill="#E7EEEE" points="23.6,19.9 15.1,19.9 15.1,27.8 23.6,27.8 23.6,33.8 33,23.9 23.6,14 "/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 759 B |
18
interface/resources/images/address-bar-submit.svg
Normal file
18
interface/resources/images/address-bar-submit.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 49 49" enable-background="new 0 0 49 49" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#0E7077" d="M49,46c0,1.7-1.3,3-3,3H3c-1.7,0-3-1.3-3-3V3c0-1.7,1.3-3,3-3h43c1.7,0,3,1.3,3,3V46z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g id="Your_Icon_11_">
|
||||
<g>
|
||||
<polygon fill="#E7EEEE" points="23.6,19.9 15.1,19.9 15.1,27.8 23.6,27.8 23.6,33.8 33,23.9 23.6,14 "/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 762 B |
|
@ -680,7 +680,7 @@ void Application::paintGL() {
|
|||
if (whichCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
OculusManager::display(whichCamera.getRotation(), whichCamera.getPosition(), whichCamera);
|
||||
} else {
|
||||
OculusManager::display(_myAvatar->getWorldAlignedOrientation(), whichCamera.getPosition(), whichCamera);
|
||||
OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), whichCamera);
|
||||
}
|
||||
|
||||
} else if (TV3DManager::isConnected()) {
|
||||
|
@ -921,12 +921,11 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
if (isMeta) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
|
||||
} else {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Chat);
|
||||
}
|
||||
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
|
||||
break;
|
||||
|
||||
case Qt::Key_Backslash:
|
||||
Menu::getInstance()->triggerOption(MenuOption::Chat);
|
||||
break;
|
||||
|
||||
case Qt::Key_N:
|
||||
|
|
|
@ -176,7 +176,7 @@ Menu::Menu() :
|
|||
SLOT(toggleLocationList()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::AddressBar,
|
||||
Qt::CTRL | Qt::Key_Enter,
|
||||
Qt::Key_Enter,
|
||||
this,
|
||||
SLOT(toggleAddressBar()));
|
||||
|
||||
|
@ -1156,22 +1156,13 @@ void Menu::changePrivateKey() {
|
|||
}
|
||||
|
||||
void Menu::toggleAddressBar() {
|
||||
|
||||
QInputDialog addressBarDialog(Application::getInstance()->getWindow());
|
||||
addressBarDialog.setWindowTitle("Address Bar");
|
||||
addressBarDialog.setWindowFlags(Qt::Sheet);
|
||||
addressBarDialog.setLabelText("place, domain, @user, example.com, /position/orientation");
|
||||
|
||||
addressBarDialog.resize(addressBarDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW,
|
||||
addressBarDialog.size().height());
|
||||
|
||||
int dialogReturn = addressBarDialog.exec();
|
||||
if (dialogReturn == QDialog::Accepted && !addressBarDialog.textValue().isEmpty()) {
|
||||
// let the AddressManger figure out what to do with this
|
||||
AddressManager::getInstance().handleLookupString(addressBarDialog.textValue());
|
||||
if (!_addressBarDialog) {
|
||||
_addressBarDialog = new AddressBarDialog();
|
||||
}
|
||||
|
||||
sendFakeEnterEvent();
|
||||
if (!_addressBarDialog->isVisible()) {
|
||||
_addressBarDialog->show();
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::displayAddressOfflineMessage() {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "SpeechRecognizer.h"
|
||||
#endif
|
||||
|
||||
#include "ui/AddressBarDialog.h"
|
||||
#include "ui/ChatWindow.h"
|
||||
#include "ui/DataWebDialog.h"
|
||||
#include "ui/JSConsole.h"
|
||||
|
@ -298,6 +299,7 @@ private:
|
|||
QPointer<PreferencesDialog> _preferencesDialog;
|
||||
QPointer<AttachmentsDialog> _attachmentsDialog;
|
||||
QPointer<AnimationsDialog> _animationsDialog;
|
||||
QPointer<AddressBarDialog> _addressBarDialog;
|
||||
QPointer<LoginDialog> _loginDialog;
|
||||
bool _hasLoginDialogDisplayed;
|
||||
QAction* _chatAction;
|
||||
|
|
|
@ -505,10 +505,11 @@ void Avatar::renderBody(RenderMode renderMode, bool postLighting, float glowLeve
|
|||
{
|
||||
Glower glower(glowLevel);
|
||||
|
||||
if ((_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) &&
|
||||
(postLighting || renderMode == SHADOW_RENDER_MODE)) {
|
||||
// render the billboard until both models are loaded
|
||||
renderBillboard();
|
||||
if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
|
||||
if (postLighting || renderMode == SHADOW_RENDER_MODE) {
|
||||
// render the billboard until both models are loaded
|
||||
renderBillboard();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -997,6 +997,10 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const {
|
|||
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f);
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::getDefaultEyePosition() const {
|
||||
return _position + getWorldAlignedOrientation() * _skeletonModel.getDefaultEyeModelPosition();
|
||||
}
|
||||
|
||||
const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f;
|
||||
const float RECORDER_PRIORITY = SCRIPT_PRIORITY + 1.0f;
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ public:
|
|||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
glm::vec3 getGravity() const { return _gravity; }
|
||||
glm::vec3 getUprightHeadPosition() const;
|
||||
glm::vec3 getDefaultEyePosition() const;
|
||||
bool getShouldRenderLocally() const { return _shouldRender; }
|
||||
|
||||
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
|
||||
|
|
|
@ -27,7 +27,8 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
|||
_owningAvatar(owningAvatar),
|
||||
_boundingShape(),
|
||||
_boundingShapeLocalOffset(0.0f),
|
||||
_ragdoll(NULL) {
|
||||
_ragdoll(NULL),
|
||||
_defaultEyeModelPosition(glm::vec3(0.f, 0.f, 0.f)) {
|
||||
}
|
||||
|
||||
SkeletonModel::~SkeletonModel() {
|
||||
|
@ -470,23 +471,23 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP
|
|||
return success;
|
||||
}
|
||||
|
||||
bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||
bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||
if (!isActive()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (getJointPositionInWorldFrame(geometry.leftEyeJointIndex, firstEyePosition) &&
|
||||
getJointPositionInWorldFrame(geometry.rightEyeJointIndex, secondEyePosition)) {
|
||||
if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) &&
|
||||
getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) {
|
||||
return true;
|
||||
}
|
||||
// no eye joints; try to estimate based on head/neck joints
|
||||
glm::vec3 neckPosition, headPosition;
|
||||
if (getJointPositionInWorldFrame(geometry.neckJointIndex, neckPosition) &&
|
||||
getJointPositionInWorldFrame(geometry.headJointIndex, headPosition)) {
|
||||
if (getJointPosition(geometry.neckJointIndex, neckPosition) &&
|
||||
getJointPosition(geometry.headJointIndex, headPosition)) {
|
||||
const float EYE_PROPORTION = 0.6f;
|
||||
glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION);
|
||||
glm::quat headRotation;
|
||||
getJointRotationInWorldFrame(geometry.headJointIndex, headRotation);
|
||||
getJointRotation(geometry.headJointIndex, headRotation);
|
||||
const float EYES_FORWARD = 0.25f;
|
||||
const float EYE_SEPARATION = 0.1f;
|
||||
float headHeight = glm::distance(neckPosition, headPosition);
|
||||
|
@ -497,6 +498,15 @@ bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& seco
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
|
||||
if (getEyeModelPositions(firstEyePosition, secondEyePosition)) {
|
||||
firstEyePosition = _translation + _rotation * firstEyePosition;
|
||||
secondEyePosition = _translation + _rotation * secondEyePosition;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SkeletonModel::renderRagdoll() {
|
||||
if (!_ragdoll) {
|
||||
return;
|
||||
|
@ -656,6 +666,20 @@ void SkeletonModel::buildShapes() {
|
|||
// This method moves the shapes to their default positions in Model frame.
|
||||
computeBoundingShape(geometry);
|
||||
|
||||
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
|
||||
if (0 <= headJointIndex && headJointIndex < _jointStates.size()) {
|
||||
glm::vec3 leftEyePosition, rightEyePosition;
|
||||
getEyeModelPositions(leftEyePosition, rightEyePosition);
|
||||
glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.f;
|
||||
|
||||
int rootJointIndex = _geometry->getFBXGeometry().rootJointIndex;
|
||||
glm::vec3 rootModelPosition;
|
||||
getJointPosition(rootJointIndex, rootModelPosition);
|
||||
|
||||
_defaultEyeModelPosition = midEyePosition - rootModelPosition;
|
||||
_defaultEyeModelPosition.z = -_defaultEyeModelPosition.z;
|
||||
}
|
||||
|
||||
// While the shapes are in their default position we disable collisions between
|
||||
// joints that are currently colliding.
|
||||
disableCurrentSelfCollisions();
|
||||
|
|
|
@ -97,6 +97,10 @@ public:
|
|||
/// \return whether or not both eye meshes were found
|
||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
||||
/// Gets the default position of the mid eye point in model frame coordinates.
|
||||
/// \return whether or not the head was found.
|
||||
glm::vec3 getDefaultEyeModelPosition() const { return _defaultEyeModelPosition; }
|
||||
|
||||
virtual void updateVisibleJointStates();
|
||||
|
||||
SkeletonRagdoll* buildRagdoll();
|
||||
|
@ -140,12 +144,16 @@ private:
|
|||
/// \param position position of joint in model-frame
|
||||
/// \param rotation rotation of joint in model-frame
|
||||
void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation);
|
||||
|
||||
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||
|
||||
Avatar* _owningAvatar;
|
||||
|
||||
CapsuleShape _boundingShape;
|
||||
glm::vec3 _boundingShapeLocalOffset;
|
||||
SkeletonRagdoll* _ragdoll;
|
||||
|
||||
glm::vec3 _defaultEyeModelPosition;
|
||||
};
|
||||
|
||||
#endif // hifi_SkeletonModel_h
|
||||
|
|
|
@ -602,6 +602,14 @@ bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) co
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
}
|
||||
rotation = _jointStates[jointIndex].getRotation();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const {
|
||||
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||
return false;
|
||||
|
|
|
@ -148,6 +148,11 @@ public:
|
|||
/// \return true if joint exists
|
||||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
|
||||
/// \param jointIndex index of joint in model structure
|
||||
/// \param rotation[out] rotation of joint in model-frame
|
||||
/// \return true if joint exists
|
||||
bool getJointRotation(int jointIndex, glm::quat& rotation) const;
|
||||
|
||||
QStringList getJointNames() const;
|
||||
|
||||
AnimationHandlePointer createAnimationHandle();
|
||||
|
|
131
interface/src/ui/AddressBarDialog.cpp
Normal file
131
interface/src/ui/AddressBarDialog.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// AddressBarDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stojce Slavkovski on 9/22/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AddressBarDialog.h"
|
||||
#include "AddressManager.h"
|
||||
#include "Application.h"
|
||||
|
||||
const QString ADDRESSBAR_GO_BUTTON_ICON = "images/address-bar-submit.svg";
|
||||
const QString ADDRESSBAR_GO_BUTTON_ACTIVE_ICON = "images/address-bar-submit-active.svg";
|
||||
|
||||
AddressBarDialog::AddressBarDialog() :
|
||||
FramelessDialog(Application::getInstance()->getWindow(), 0, FramelessDialog::POSITION_TOP) {
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose, false);
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void AddressBarDialog::setupUI() {
|
||||
|
||||
const QString DIALOG_STYLESHEET = "font-family: Helvetica, Arial, sans-serif;";
|
||||
const QString ADDRESSBAR_PLACEHOLDER = "Go to: domain, @user, #location";
|
||||
const QString ADDRESSBAR_STYLESHEET = "padding: 0 10px;";
|
||||
const QString ADDRESSBAR_FONT_FAMILY = "Helvetica,Arial,sans-serif";
|
||||
const int ADDRESSBAR_FONT_SIZE = 20;
|
||||
|
||||
const int ADDRESSBAR_MIN_WIDTH = 200;
|
||||
const int ADDRESSBAR_MAX_WIDTH = 615;
|
||||
const int ADDRESSBAR_HEIGHT = 54;
|
||||
const int ADDRESSBAR_STRETCH = 60;
|
||||
|
||||
const int BUTTON_SPACER_SIZE = 10;
|
||||
const int DEFAULT_SPACER_SIZE = 20;
|
||||
const int ADDRESS_LAYOUT_RIGHT_MARGIN = 10;
|
||||
|
||||
const int GO_BUTTON_SIZE = 55;
|
||||
const int CLOSE_BUTTON_SIZE = 16;
|
||||
const QString CLOSE_BUTTON_ICON = "styles/close.svg";
|
||||
|
||||
const int DIALOG_HEIGHT = 100;
|
||||
const int DIALOG_INITIAL_WIDTH = 560;
|
||||
|
||||
setModal(true);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
setHideOnBlur(false);
|
||||
|
||||
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
setSizePolicy(sizePolicy);
|
||||
setMinimumSize(QSize(DIALOG_INITIAL_WIDTH, DIALOG_HEIGHT));
|
||||
setStyleSheet(DIALOG_STYLESHEET);
|
||||
|
||||
_verticalLayout = new QVBoxLayout(this);
|
||||
|
||||
_addressLayout = new QHBoxLayout();
|
||||
_addressLayout->setContentsMargins(0, 0, ADDRESS_LAYOUT_RIGHT_MARGIN, 0);
|
||||
|
||||
_leftSpacer = new QSpacerItem(DEFAULT_SPACER_SIZE,
|
||||
DEFAULT_SPACER_SIZE,
|
||||
QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Minimum);
|
||||
|
||||
_addressLayout->addItem(_leftSpacer);
|
||||
|
||||
_addressLineEdit = new QLineEdit(this);
|
||||
_addressLineEdit->setPlaceholderText(ADDRESSBAR_PLACEHOLDER);
|
||||
QSizePolicy sizePolicyLineEdit(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
sizePolicyLineEdit.setHorizontalStretch(ADDRESSBAR_STRETCH);
|
||||
_addressLineEdit->setSizePolicy(sizePolicyLineEdit);
|
||||
_addressLineEdit->setMinimumSize(QSize(ADDRESSBAR_MIN_WIDTH, ADDRESSBAR_HEIGHT));
|
||||
_addressLineEdit->setMaximumSize(QSize(ADDRESSBAR_MAX_WIDTH, ADDRESSBAR_HEIGHT));
|
||||
QFont font(ADDRESSBAR_FONT_FAMILY, ADDRESSBAR_FONT_SIZE);
|
||||
_addressLineEdit->setFont(font);
|
||||
_addressLineEdit->setStyleSheet(ADDRESSBAR_STYLESHEET);
|
||||
_addressLayout->addWidget(_addressLineEdit);
|
||||
|
||||
_buttonSpacer = new QSpacerItem(BUTTON_SPACER_SIZE, BUTTON_SPACER_SIZE, QSizePolicy::Fixed, QSizePolicy::Minimum);
|
||||
_addressLayout->addItem(_buttonSpacer);
|
||||
|
||||
_goButton = new QPushButton(this);
|
||||
_goButton->setSizePolicy(sizePolicy);
|
||||
_goButton->setMinimumSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE));
|
||||
_goButton->setMaximumSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE));
|
||||
_goButton->setIcon(QIcon(Application::resourcesPath() + ADDRESSBAR_GO_BUTTON_ICON));
|
||||
_goButton->setIconSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE));
|
||||
_goButton->setDefault(true);
|
||||
_goButton->setFlat(true);
|
||||
_addressLayout->addWidget(_goButton);
|
||||
|
||||
_rightSpacer = new QSpacerItem(DEFAULT_SPACER_SIZE,
|
||||
DEFAULT_SPACER_SIZE,
|
||||
QSizePolicy::MinimumExpanding,
|
||||
QSizePolicy::Minimum);
|
||||
|
||||
_addressLayout->addItem(_rightSpacer);
|
||||
|
||||
_closeButton = new QPushButton(this);
|
||||
_closeButton->setSizePolicy(sizePolicy);
|
||||
_closeButton->setMinimumSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE));
|
||||
_closeButton->setMaximumSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE));
|
||||
QIcon icon(Application::resourcesPath() + CLOSE_BUTTON_ICON);
|
||||
_closeButton->setIcon(icon);
|
||||
_closeButton->setFlat(true);
|
||||
_addressLayout->addWidget(_closeButton, 0, Qt::AlignRight);
|
||||
|
||||
_verticalLayout->addLayout(_addressLayout);
|
||||
|
||||
connect(_goButton, &QPushButton::clicked, this, &AddressBarDialog::accept);
|
||||
connect(_closeButton, &QPushButton::clicked, this, &QDialog::close);
|
||||
}
|
||||
|
||||
void AddressBarDialog::showEvent(QShowEvent* event) {
|
||||
_goButton->setIcon(QIcon(Application::resourcesPath() + ADDRESSBAR_GO_BUTTON_ICON));
|
||||
_addressLineEdit->setText(QString());
|
||||
FramelessDialog::showEvent(event);
|
||||
}
|
||||
|
||||
void AddressBarDialog::accept() {
|
||||
if (!_addressLineEdit->text().isEmpty()) {
|
||||
_goButton->setIcon(QIcon(Application::resourcesPath() + ADDRESSBAR_GO_BUTTON_ACTIVE_ICON));
|
||||
AddressManager& addressManager = AddressManager::getInstance();
|
||||
connect(&addressManager, &AddressManager::lookupResultsFinished, this, &QDialog::hide);
|
||||
addressManager.handleLookupString(_addressLineEdit->text());
|
||||
}
|
||||
}
|
46
interface/src/ui/AddressBarDialog.h
Normal file
46
interface/src/ui/AddressBarDialog.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// AddressBarDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stojce Slavkovski on 9/22/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AddressBarDialog_h
|
||||
#define hifi_AddressBarDialog_h
|
||||
|
||||
#include "FramelessDialog.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class AddressBarDialog : public FramelessDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AddressBarDialog();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void showEvent(QShowEvent* event);
|
||||
|
||||
QVBoxLayout *_verticalLayout;
|
||||
QHBoxLayout *_addressLayout;
|
||||
QSpacerItem *_leftSpacer;
|
||||
QSpacerItem *_rightSpacer;
|
||||
QSpacerItem *_buttonSpacer;
|
||||
QPushButton *_goButton;
|
||||
QPushButton *_closeButton;
|
||||
QLineEdit *_addressLineEdit;
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_AddressBarDialog_h
|
|
@ -25,7 +25,8 @@ AudioInjector::AudioInjector(QObject* parent) :
|
|||
QObject(parent),
|
||||
_sound(NULL),
|
||||
_options(),
|
||||
_shouldStop(false)
|
||||
_shouldStop(false),
|
||||
_currentSendPosition(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -77,11 +78,13 @@ void AudioInjector::injectAudio() {
|
|||
|
||||
// pack the position for injected audio
|
||||
int positionOptionOffset = injectAudioPacket.size();
|
||||
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getPosition()), sizeof(_options.getPosition()));
|
||||
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getPosition()),
|
||||
sizeof(_options.getPosition()));
|
||||
|
||||
// pack our orientation for injected audio
|
||||
int orientationOptionOffset = injectAudioPacket.size();
|
||||
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getOrientation()), sizeof(_options.getOrientation()));
|
||||
packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getOrientation()),
|
||||
sizeof(_options.getOrientation()));
|
||||
|
||||
// pack zero for radius
|
||||
float radius = 0;
|
||||
|
@ -95,17 +98,15 @@ void AudioInjector::injectAudio() {
|
|||
timer.start();
|
||||
int nextFrame = 0;
|
||||
|
||||
int currentSendPosition = 0;
|
||||
|
||||
int numPreAudioDataBytes = injectAudioPacket.size();
|
||||
bool shouldLoop = _options.getLoop();
|
||||
|
||||
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
|
||||
quint16 outgoingInjectedAudioSequenceNumber = 0;
|
||||
while (currentSendPosition < soundByteArray.size() && !_shouldStop) {
|
||||
while (_currentSendPosition < soundByteArray.size() && !_shouldStop) {
|
||||
|
||||
int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
|
||||
soundByteArray.size() - currentSendPosition);
|
||||
int bytesToCopy = std::min(((_options.isStereo()) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
|
||||
soundByteArray.size() - _currentSendPosition);
|
||||
memcpy(injectAudioPacket.data() + positionOptionOffset,
|
||||
&_options.getPosition(),
|
||||
sizeof(_options.getPosition()));
|
||||
|
@ -117,10 +118,12 @@ void AudioInjector::injectAudio() {
|
|||
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);
|
||||
|
||||
// pack the sequence number
|
||||
memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes, &outgoingInjectedAudioSequenceNumber, sizeof(quint16));
|
||||
memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes,
|
||||
&outgoingInjectedAudioSequenceNumber, sizeof(quint16));
|
||||
|
||||
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
|
||||
memcpy(injectAudioPacket.data() + numPreAudioDataBytes, soundByteArray.data() + currentSendPosition, bytesToCopy);
|
||||
memcpy(injectAudioPacket.data() + numPreAudioDataBytes,
|
||||
soundByteArray.data() + _currentSendPosition, bytesToCopy);
|
||||
|
||||
// grab our audio mixer from the NodeList, if it exists
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
@ -130,22 +133,22 @@ void AudioInjector::injectAudio() {
|
|||
nodeList->writeDatagram(injectAudioPacket, audioMixer);
|
||||
outgoingInjectedAudioSequenceNumber++;
|
||||
|
||||
currentSendPosition += bytesToCopy;
|
||||
_currentSendPosition += bytesToCopy;
|
||||
|
||||
// send two packets before the first sleep so the mixer can start playback right away
|
||||
|
||||
if (currentSendPosition != bytesToCopy && currentSendPosition < soundByteArray.size()) {
|
||||
if (_currentSendPosition != bytesToCopy && _currentSendPosition < soundByteArray.size()) {
|
||||
// not the first packet and not done
|
||||
// sleep for the appropriate time
|
||||
int usecToSleep = (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - timer.nsecsElapsed() / 1000;
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldLoop && currentSendPosition == soundByteArray.size()) {
|
||||
currentSendPosition = 0;
|
||||
if (shouldLoop && _currentSendPosition >= soundByteArray.size()) {
|
||||
_currentSendPosition = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,16 +26,20 @@ class AudioInjector : public QObject {
|
|||
public:
|
||||
AudioInjector(QObject* parent);
|
||||
AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions);
|
||||
|
||||
int getCurrentSendPosition() const { return _currentSendPosition; }
|
||||
public slots:
|
||||
void injectAudio();
|
||||
void stop() { _shouldStop = true; }
|
||||
void setOptions(AudioInjectorOptions& options);
|
||||
void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; }
|
||||
signals:
|
||||
void finished();
|
||||
private:
|
||||
Sound* _sound;
|
||||
AudioInjectorOptions _options;
|
||||
bool _shouldStop;
|
||||
int _currentSendPosition;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AudioInjector*)
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
|
||||
AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) {
|
||||
|
||||
if (sound->isStereo()) {
|
||||
const_cast<AudioInjectorOptions*>(injectorOptions)->setIsStereo(true);
|
||||
}
|
||||
AudioInjector* injector = new AudioInjector(sound, *injectorOptions);
|
||||
|
||||
QThread* injectorThread = new QThread();
|
||||
|
|
|
@ -30,7 +30,9 @@ InjectedAudioStream::InjectedAudioStream(const QUuid& streamIdentifier, const In
|
|||
|
||||
const uchar MAX_INJECTOR_VOLUME = 255;
|
||||
|
||||
int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) {
|
||||
int InjectedAudioStream::parseStreamProperties(PacketType type,
|
||||
const QByteArray& packetAfterSeqNum,
|
||||
int& numAudioSamples) {
|
||||
// setup a data stream to read from this packet
|
||||
QDataStream packetStream(packetAfterSeqNum);
|
||||
|
||||
|
@ -38,6 +40,9 @@ int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray
|
|||
packetStream.skipRawData(NUM_BYTES_RFC4122_UUID);
|
||||
|
||||
packetStream >> _isStereo;
|
||||
if (isStereo()) {
|
||||
_ringBuffer.resizeForFrameSize(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO);
|
||||
}
|
||||
|
||||
// pull the loopback flag and set our boolean
|
||||
uchar shouldLoopback;
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
|
||||
// procedural audio version of Sound
|
||||
Sound::Sound(float volume, float frequency, float duration, float decay, QObject* parent) :
|
||||
QObject(parent)
|
||||
QObject(parent),
|
||||
_isStereo(false)
|
||||
{
|
||||
static char monoAudioData[MAX_PACKET_SIZE];
|
||||
static int16_t* monoAudioSamples = (int16_t*)(monoAudioData);
|
||||
|
@ -69,8 +70,9 @@ Sound::Sound(float volume, float frequency, float duration, float decay, QObject
|
|||
}
|
||||
}
|
||||
|
||||
Sound::Sound(const QUrl& sampleURL, QObject* parent) :
|
||||
Sound::Sound(const QUrl& sampleURL, bool isStereo, QObject* parent) :
|
||||
QObject(parent),
|
||||
_isStereo(isStereo),
|
||||
_hasDownloaded(false)
|
||||
{
|
||||
// assume we have a QApplication or QCoreApplication instance and use the
|
||||
|
@ -82,12 +84,14 @@ Sound::Sound(const QUrl& sampleURL, QObject* parent) :
|
|||
|
||||
QNetworkReply* soundDownload = networkAccessManager.get(QNetworkRequest(sampleURL));
|
||||
connect(soundDownload, &QNetworkReply::finished, this, &Sound::replyFinished);
|
||||
connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError)));
|
||||
connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
this, SLOT(replyError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
||||
Sound::Sound(const QByteArray byteArray, QObject* parent) :
|
||||
QObject(parent),
|
||||
_byteArray(byteArray),
|
||||
_isStereo(false),
|
||||
_hasDownloaded(true)
|
||||
{
|
||||
}
|
||||
|
@ -149,11 +153,20 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) {
|
|||
int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data();
|
||||
int16_t* destinationSamples = (int16_t*) _byteArray.data();
|
||||
|
||||
for (int i = 1; i < numSourceSamples; i += 2) {
|
||||
if (i + 1 >= numSourceSamples) {
|
||||
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 2) + (sourceSamples[i] / 2);
|
||||
} else {
|
||||
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 4);
|
||||
|
||||
if (_isStereo) {
|
||||
for (int i = 0; i < numSourceSamples; i += 4) {
|
||||
destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2);
|
||||
destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2);
|
||||
}
|
||||
} else {
|
||||
for (int i = 1; i < numSourceSamples; i += 2) {
|
||||
if (i + 1 >= numSourceSamples) {
|
||||
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 2) + (sourceSamples[i] / 2);
|
||||
} else {
|
||||
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2)
|
||||
+ (sourceSamples[i + 1] / 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,17 +20,19 @@ class Sound : public QObject {
|
|||
|
||||
Q_PROPERTY(bool downloaded READ hasDownloaded)
|
||||
public:
|
||||
Sound(const QUrl& sampleURL, QObject* parent = NULL);
|
||||
Sound(const QUrl& sampleURL, bool isStereo = false, QObject* parent = NULL);
|
||||
Sound(float volume, float frequency, float duration, float decay, QObject* parent = NULL);
|
||||
Sound(const QByteArray byteArray, QObject* parent = NULL);
|
||||
void append(const QByteArray byteArray);
|
||||
|
||||
bool isStereo() const { return _isStereo; }
|
||||
bool hasDownloaded() const { return _hasDownloaded; }
|
||||
|
||||
const QByteArray& getByteArray() { return _byteArray; }
|
||||
|
||||
private:
|
||||
QByteArray _byteArray;
|
||||
bool _isStereo;
|
||||
bool _hasDownloaded;
|
||||
|
||||
void trimFrames();
|
||||
|
|
|
@ -587,18 +587,13 @@ bool AvatarData::hasReferential() {
|
|||
}
|
||||
|
||||
bool AvatarData::isPlaying() {
|
||||
if (!_player) {
|
||||
return false;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(this, "isPlaying", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, result));
|
||||
return result;
|
||||
}
|
||||
return _player && _player->isPlaying();
|
||||
}
|
||||
|
||||
bool AvatarData::isPaused() {
|
||||
return _player && _player->isPaused();
|
||||
}
|
||||
|
||||
qint64 AvatarData::playerElapsed() {
|
||||
if (!_player) {
|
||||
return 0;
|
||||
|
@ -625,6 +620,14 @@ qint64 AvatarData::playerLength() {
|
|||
return _player->getRecording()->getLength();
|
||||
}
|
||||
|
||||
int AvatarData::playerCurrentFrame() {
|
||||
return (_player) ? _player->getCurrentFrame() : 0;
|
||||
}
|
||||
|
||||
int AvatarData::playerFrameNumber() {
|
||||
return (_player && _player->getRecording()) ? _player->getRecording()->getFrameNumber() : 0;
|
||||
}
|
||||
|
||||
void AvatarData::loadRecording(QString filename) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection,
|
||||
|
@ -649,6 +652,18 @@ void AvatarData::startPlaying() {
|
|||
_player->startPlaying();
|
||||
}
|
||||
|
||||
void AvatarData::setPlayerFrame(int frame) {
|
||||
if (_player) {
|
||||
_player->setCurrentFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarData::setPlayerTime(qint64 time) {
|
||||
if (_player) {
|
||||
_player->setCurrentTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarData::setPlayFromCurrentLocation(bool playFromCurrentLocation) {
|
||||
if (_player) {
|
||||
_player->setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
|
@ -696,6 +711,19 @@ void AvatarData::play() {
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarData::pausePlayer() {
|
||||
if (!_player) {
|
||||
return;
|
||||
}
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection);
|
||||
return;
|
||||
}
|
||||
if (_player) {
|
||||
_player->pausePlayer();
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarData::stopPlaying() {
|
||||
if (!_player) {
|
||||
return;
|
||||
|
|
|
@ -296,10 +296,16 @@ public slots:
|
|||
bool hasReferential();
|
||||
|
||||
bool isPlaying();
|
||||
bool isPaused();
|
||||
qint64 playerElapsed();
|
||||
qint64 playerLength();
|
||||
int playerCurrentFrame();
|
||||
int playerFrameNumber();
|
||||
|
||||
void loadRecording(QString filename);
|
||||
void startPlaying();
|
||||
void setPlayerFrame(int frame);
|
||||
void setPlayerTime(qint64 time);
|
||||
void setPlayFromCurrentLocation(bool playFromCurrentLocation);
|
||||
void setPlayerLoop(bool loop);
|
||||
void setPlayerUseDisplayName(bool useDisplayName);
|
||||
|
@ -307,6 +313,7 @@ public slots:
|
|||
void setPlayerUseHeadModel(bool useHeadModel);
|
||||
void setPlayerUseSkeletonModel(bool useSkeletonModel);
|
||||
void play();
|
||||
void pausePlayer();
|
||||
void stopPlaying();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <AudioRingBuffer.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <NodeList.h>
|
||||
#include <StreamUtils.h>
|
||||
|
@ -18,6 +19,8 @@
|
|||
|
||||
Player::Player(AvatarData* avatar) :
|
||||
_recording(new Recording()),
|
||||
_pausedFrame(-1),
|
||||
_timerOffset(0),
|
||||
_avatar(avatar),
|
||||
_audioThread(NULL),
|
||||
_playFromCurrentPosition(true),
|
||||
|
@ -36,16 +39,26 @@ bool Player::isPlaying() const {
|
|||
return _timer.isValid();
|
||||
}
|
||||
|
||||
bool Player::isPaused() const {
|
||||
return (_pausedFrame != -1);
|
||||
}
|
||||
|
||||
qint64 Player::elapsed() const {
|
||||
if (isPlaying()) {
|
||||
return _timer.elapsed();
|
||||
return _timerOffset + _timer.elapsed();
|
||||
} else if (isPaused()) {
|
||||
return _timerOffset;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::startPlaying() {
|
||||
if (_recording && _recording->getFrameNumber() > 0) {
|
||||
if (!_recording || _recording->getFrameNumber() <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPaused()) {
|
||||
_currentContext.globalTimestamp = usecTimestampNow();
|
||||
_currentContext.domain = NodeList::getInstance()->getDomainHandler().getHostname();
|
||||
_currentContext.position = _avatar->getPosition();
|
||||
|
@ -97,9 +110,17 @@ void Player::startPlaying() {
|
|||
_avatar->setForceFaceshiftConnected(true);
|
||||
|
||||
qDebug() << "Recorder::startPlaying()";
|
||||
setupAudioThread();
|
||||
_currentFrame = 0;
|
||||
_timerOffset = 0;
|
||||
_timer.start();
|
||||
} else {
|
||||
qDebug() << "Recorder::startPlaying(): Unpause";
|
||||
setupAudioThread();
|
||||
_timer.start();
|
||||
|
||||
setCurrentFrame(_pausedFrame);
|
||||
_pausedFrame = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,6 +128,7 @@ void Player::stopPlaying() {
|
|||
if (!isPlaying()) {
|
||||
return;
|
||||
}
|
||||
_pausedFrame = -1;
|
||||
_timer.invalidate();
|
||||
cleanupAudioThread();
|
||||
_avatar->clearJointsData();
|
||||
|
@ -130,6 +152,15 @@ void Player::stopPlaying() {
|
|||
qDebug() << "Recorder::stopPlaying()";
|
||||
}
|
||||
|
||||
void Player::pausePlayer() {
|
||||
_timerOffset = elapsed();
|
||||
_timer.invalidate();
|
||||
cleanupAudioThread();
|
||||
|
||||
_pausedFrame = _currentFrame;
|
||||
qDebug() << "Recorder::pausePlayer()";
|
||||
}
|
||||
|
||||
void Player::setupAudioThread() {
|
||||
_audioThread = new QThread();
|
||||
_options.setPosition(_avatar->getPosition());
|
||||
|
@ -156,6 +187,7 @@ void Player::loopRecording() {
|
|||
cleanupAudioThread();
|
||||
setupAudioThread();
|
||||
_currentFrame = 0;
|
||||
_timerOffset = 0;
|
||||
_timer.restart();
|
||||
}
|
||||
|
||||
|
@ -166,10 +198,13 @@ void Player::loadFromFile(const QString& file) {
|
|||
_recording = RecordingPointer(new Recording());
|
||||
}
|
||||
readRecordingFromFile(_recording, file);
|
||||
|
||||
_pausedFrame = -1;
|
||||
}
|
||||
|
||||
void Player::loadRecording(RecordingPointer recording) {
|
||||
_recording = recording;
|
||||
_pausedFrame = -1;
|
||||
}
|
||||
|
||||
void Player::play() {
|
||||
|
@ -213,6 +248,77 @@ void Player::play() {
|
|||
_injector->setOptions(_options);
|
||||
}
|
||||
|
||||
void Player::setCurrentFrame(int currentFrame) {
|
||||
if (_recording && (currentFrame < 0 || currentFrame >= _recording->getFrameNumber())) {
|
||||
stopPlaying();
|
||||
return;
|
||||
}
|
||||
|
||||
_currentFrame = currentFrame;
|
||||
_timerOffset = _recording->getFrameTimestamp(_currentFrame);
|
||||
|
||||
if (isPlaying()) {
|
||||
_timer.start();
|
||||
setAudionInjectorPosition();
|
||||
} else {
|
||||
_pausedFrame = currentFrame;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::setCurrentTime(qint64 currentTime) {
|
||||
if (currentTime < 0 || currentTime >= _recording->getLength()) {
|
||||
stopPlaying();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find correct frame
|
||||
int lowestBound = 0;
|
||||
int highestBound = _recording->getFrameNumber() - 1;
|
||||
while (lowestBound + 1 != highestBound) {
|
||||
assert(lowestBound < highestBound);
|
||||
|
||||
int bestGuess = lowestBound +
|
||||
(highestBound - lowestBound) *
|
||||
(float)(currentTime - _recording->getFrameTimestamp(lowestBound)) /
|
||||
(float)(_recording->getFrameTimestamp(highestBound) - _recording->getFrameTimestamp(lowestBound));
|
||||
|
||||
if (_recording->getFrameTimestamp(bestGuess) <= currentTime) {
|
||||
if (currentTime < _recording->getFrameTimestamp(bestGuess + 1)) {
|
||||
lowestBound = bestGuess;
|
||||
highestBound = bestGuess + 1;
|
||||
} else {
|
||||
lowestBound = bestGuess + 1;
|
||||
}
|
||||
} else {
|
||||
if (_recording->getFrameTimestamp(bestGuess - 1) <= currentTime) {
|
||||
lowestBound = bestGuess - 1;
|
||||
highestBound = bestGuess;
|
||||
} else {
|
||||
highestBound = bestGuess - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_currentFrame = lowestBound;
|
||||
_timerOffset = _recording->getFrameTimestamp(lowestBound);
|
||||
|
||||
if (isPlaying()) {
|
||||
_timer.start();
|
||||
setAudionInjectorPosition();
|
||||
} else {
|
||||
_pausedFrame = lowestBound;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::setAudionInjectorPosition() {
|
||||
int MSEC_PER_SEC = 1000;
|
||||
int SAMPLE_SIZE = 2; // 16 bits
|
||||
int CHANNEL_COUNT = 1;
|
||||
int FRAME_SIZE = SAMPLE_SIZE * CHANNEL_COUNT;
|
||||
int currentAudioFrame = elapsed() * FRAME_SIZE * (SAMPLE_RATE / MSEC_PER_SEC);
|
||||
_injector->setCurrentSendPosition(currentAudioFrame);
|
||||
}
|
||||
|
||||
void Player::setPlayFromCurrentLocation(bool playFromCurrentLocation) {
|
||||
_playFromCurrentPosition = playFromCurrentLocation;
|
||||
}
|
||||
|
@ -227,7 +333,7 @@ bool Player::computeCurrentFrame() {
|
|||
}
|
||||
|
||||
while (_currentFrame < _recording->getFrameNumber() - 1 &&
|
||||
_recording->getFrameTimestamp(_currentFrame) < _timer.elapsed()) {
|
||||
_recording->getFrameTimestamp(_currentFrame) < elapsed()) {
|
||||
++_currentFrame;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,17 +30,23 @@ public:
|
|||
Player(AvatarData* avatar);
|
||||
|
||||
bool isPlaying() const;
|
||||
bool isPaused() const;
|
||||
qint64 elapsed() const;
|
||||
|
||||
RecordingPointer getRecording() const { return _recording; }
|
||||
int getCurrentFrame() const { return _currentFrame; }
|
||||
|
||||
public slots:
|
||||
void startPlaying();
|
||||
void stopPlaying();
|
||||
void pausePlayer();
|
||||
void loadFromFile(const QString& file);
|
||||
void loadRecording(RecordingPointer recording);
|
||||
void play();
|
||||
|
||||
void setCurrentFrame(int currentFrame);
|
||||
void setCurrentTime(qint64 currentTime);
|
||||
|
||||
void setPlayFromCurrentLocation(bool playFromCurrentPosition);
|
||||
void setLoop(bool loop) { _loop = loop; }
|
||||
void useAttachements(bool useAttachments) { _useAttachments = useAttachments; }
|
||||
|
@ -52,11 +58,14 @@ private:
|
|||
void setupAudioThread();
|
||||
void cleanupAudioThread();
|
||||
void loopRecording();
|
||||
void setAudionInjectorPosition();
|
||||
bool computeCurrentFrame();
|
||||
|
||||
QElapsedTimer _timer;
|
||||
RecordingPointer _recording;
|
||||
int _currentFrame;
|
||||
int _pausedFrame;
|
||||
qint64 _timerOffset;
|
||||
|
||||
QSharedPointer<AudioInjector> _injector;
|
||||
AudioInjectorOptions _options;
|
||||
|
@ -64,7 +73,6 @@ private:
|
|||
AvatarData* _avatar;
|
||||
QThread* _audioThread;
|
||||
|
||||
|
||||
RecordingContext _currentContext;
|
||||
bool _playFromCurrentPosition;
|
||||
bool _loop;
|
||||
|
@ -72,7 +80,6 @@ private:
|
|||
bool _useDisplayName;
|
||||
bool _useHeadURL;
|
||||
bool _useSkeletonURL;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_Player_h
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <AudioRingBuffer.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
|
@ -60,6 +61,9 @@ qint32 Recording::getFrameTimestamp(int i) const {
|
|||
if (i >= _timestamps.size()) {
|
||||
return getLength();
|
||||
}
|
||||
if (i < 0) {
|
||||
return 0;
|
||||
}
|
||||
return _timestamps[i];
|
||||
}
|
||||
|
||||
|
@ -781,7 +785,6 @@ RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QStr
|
|||
fileStream >> audioArray;
|
||||
|
||||
// Cut down audio if necessary
|
||||
int SAMPLE_RATE = 48000; // 48 kHz
|
||||
int SAMPLE_SIZE = 2; // 16 bits
|
||||
int MSEC_PER_SEC = 1000;
|
||||
int audioLength = recording->getLength() * SAMPLE_SIZE * (SAMPLE_RATE / MSEC_PER_SEC);
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
#include <AbstractAudioInterface.h>
|
||||
#include <VoxelTree.h>
|
||||
#include <AvatarData.h>
|
||||
#include <CollisionInfo.h>
|
||||
#include <HeadData.h>
|
||||
#include <HandData.h>
|
||||
#include <SphereShape.h>
|
||||
|
||||
#include "EntityItem.h"
|
||||
#include "EntityCollisionSystem.h"
|
||||
|
@ -103,11 +105,33 @@ void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) {
|
|||
}
|
||||
|
||||
void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
||||
glm::vec3 center = entityA->getPosition() * (float)(TREE_SCALE);
|
||||
float radius = entityA->getRadius() * (float)(TREE_SCALE);
|
||||
|
||||
if (entityA->getIgnoreForCollisions()) {
|
||||
return; // bail early if this entity is to be ignored...
|
||||
}
|
||||
|
||||
glm::vec3 penetration;
|
||||
EntityItem* entityB;
|
||||
if (_entities->findSpherePenetration(center, radius, penetration, (void**)&entityB, Octree::NoLock)) {
|
||||
EntityItem* entityB = NULL;
|
||||
|
||||
const int MAX_COLLISIONS_PER_ENTITY = 32;
|
||||
CollisionList collisions(MAX_COLLISIONS_PER_ENTITY);
|
||||
bool shapeCollisionsAccurate = false;
|
||||
|
||||
bool shapeCollisions = _entities->findShapeCollisions(&entityA->getCollisionShapeInMeters(),
|
||||
collisions, Octree::NoLock, &shapeCollisionsAccurate);
|
||||
|
||||
if (shapeCollisions) {
|
||||
for(int i = 0; i < collisions.size(); i++) {
|
||||
CollisionInfo* collision = collisions[i];
|
||||
penetration = collision->_penetration;
|
||||
entityB = static_cast<EntityItem*>(collision->_extraData);
|
||||
|
||||
// TODO: how to handle multiple collisions?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shapeCollisions) {
|
||||
// NOTE: 'penetration' is the depth that 'entityA' overlaps 'entityB'. It points from A into B.
|
||||
glm::vec3 penetrationInTreeUnits = penetration / (float)(TREE_SCALE);
|
||||
|
||||
|
@ -115,8 +139,13 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
|||
// we don't want to count this as a collision.
|
||||
glm::vec3 relativeVelocity = entityA->getVelocity() - entityB->getVelocity();
|
||||
|
||||
bool wantToMoveA = entityA->getCollisionsWillMove();
|
||||
bool wantToMoveB = entityB->getCollisionsWillMove();
|
||||
bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f;
|
||||
bool doCollisions = movingTowardEachOther; // don't do collisions if the entities are moving away from each other
|
||||
|
||||
// only do collisions if the entities are moving toward each other and one or the other
|
||||
// of the entities are movable from collisions
|
||||
bool doCollisions = movingTowardEachOther && (wantToMoveA || wantToMoveB);
|
||||
|
||||
if (doCollisions) {
|
||||
quint64 now = usecTimestampNow();
|
||||
|
@ -133,35 +162,53 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
|
|||
float massA = entityA->getMass();
|
||||
float massB = entityB->getMass();
|
||||
float totalMass = massA + massB;
|
||||
float massRatioA = (2.0f * massB / totalMass);
|
||||
float massRatioB = (2.0f * massA / totalMass);
|
||||
|
||||
// handle Entity A
|
||||
glm::vec3 newVelocityA = entityA->getVelocity() - axialVelocity * (2.0f * massB / totalMass);
|
||||
glm::vec3 newPositionA = entityA->getPosition() - 0.5f * penetrationInTreeUnits;
|
||||
// in the event that one of our entities is non-moving, then fix up these ratios
|
||||
if (wantToMoveA && !wantToMoveB) {
|
||||
massRatioA = 2.0f;
|
||||
massRatioB = 0.0f;
|
||||
}
|
||||
|
||||
EntityItemProperties propertiesA = entityA->getProperties();
|
||||
EntityItemID idA(entityA->getID());
|
||||
propertiesA.setVelocity(newVelocityA * (float)TREE_SCALE);
|
||||
propertiesA.setPosition(newPositionA * (float)TREE_SCALE);
|
||||
propertiesA.setLastEdited(now);
|
||||
if (!wantToMoveA && wantToMoveB) {
|
||||
massRatioA = 0.0f;
|
||||
massRatioB = 2.0f;
|
||||
}
|
||||
|
||||
_entities->updateEntity(idA, propertiesA);
|
||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA);
|
||||
// unless the entity is configured to not be moved by collision, calculate it's new position
|
||||
// and velocity and apply it
|
||||
if (wantToMoveA) {
|
||||
// handle Entity A
|
||||
glm::vec3 newVelocityA = entityA->getVelocity() - axialVelocity * massRatioA;
|
||||
glm::vec3 newPositionA = entityA->getPosition() - 0.5f * penetrationInTreeUnits;
|
||||
|
||||
glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * (2.0f * massA / totalMass);
|
||||
glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits;
|
||||
EntityItemProperties propertiesA = entityA->getProperties();
|
||||
EntityItemID idA(entityA->getID());
|
||||
propertiesA.setVelocity(newVelocityA * (float)TREE_SCALE);
|
||||
propertiesA.setPosition(newPositionA * (float)TREE_SCALE);
|
||||
propertiesA.setLastEdited(now);
|
||||
|
||||
_entities->updateEntity(idA, propertiesA);
|
||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA);
|
||||
}
|
||||
|
||||
EntityItemProperties propertiesB = entityB->getProperties();
|
||||
// unless the entity is configured to not be moved by collision, calculate it's new position
|
||||
// and velocity and apply it
|
||||
if (wantToMoveB) {
|
||||
glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * massRatioB;
|
||||
glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits;
|
||||
|
||||
EntityItemID idB(entityB->getID());
|
||||
propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE);
|
||||
propertiesB.setPosition(newPositionB * (float)TREE_SCALE);
|
||||
propertiesB.setLastEdited(now);
|
||||
EntityItemProperties propertiesB = entityB->getProperties();
|
||||
|
||||
_entities->updateEntity(idB, propertiesB);
|
||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB);
|
||||
|
||||
// TODO: Do we need this?
|
||||
//_packetSender->releaseQueuedMessages();
|
||||
EntityItemID idB(entityB->getID());
|
||||
propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE);
|
||||
propertiesB.setPosition(newPositionB * (float)TREE_SCALE);
|
||||
propertiesB.setLastEdited(now);
|
||||
|
||||
_entities->updateEntity(idB, propertiesB);
|
||||
_packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ const glm::vec3 EntityItem::NO_ANGULAR_VELOCITY = glm::vec3(0.0f, 0.0f, 0.0f);
|
|||
const glm::vec3 EntityItem::DEFAULT_ANGULAR_VELOCITY = NO_ANGULAR_VELOCITY;
|
||||
const float EntityItem::DEFAULT_ANGULAR_DAMPING = 0.5f;
|
||||
const bool EntityItem::DEFAULT_VISIBLE = true;
|
||||
const bool EntityItem::DEFAULT_IGNORE_FOR_COLLISIONS = false;
|
||||
const bool EntityItem::DEFAULT_COLLISIONS_WILL_MOVE = false;
|
||||
|
||||
void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
|
||||
_id = entityItemID.id;
|
||||
|
@ -70,6 +72,10 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
|
|||
_angularVelocity = DEFAULT_ANGULAR_VELOCITY;
|
||||
_angularDamping = DEFAULT_ANGULAR_DAMPING;
|
||||
_visible = DEFAULT_VISIBLE;
|
||||
_ignoreForCollisions = DEFAULT_IGNORE_FOR_COLLISIONS;
|
||||
_collisionsWillMove = DEFAULT_COLLISIONS_WILL_MOVE;
|
||||
|
||||
recalculateCollisionShape();
|
||||
}
|
||||
|
||||
EntityItem::EntityItem(const EntityItemID& entityItemID) {
|
||||
|
@ -109,6 +115,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
requestedProperties += PROP_ANGULAR_VELOCITY;
|
||||
requestedProperties += PROP_ANGULAR_DAMPING;
|
||||
requestedProperties += PROP_VISIBLE;
|
||||
requestedProperties += PROP_IGNORE_FOR_COLLISIONS;
|
||||
requestedProperties += PROP_COLLISIONS_WILL_MOVE;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
@ -222,6 +230,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, getAngularVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, getAngularDamping());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, getVisible());
|
||||
APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, appendValue, getIgnoreForCollisions());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, getCollisionsWillMove());
|
||||
|
||||
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
|
||||
requestedProperties,
|
||||
|
@ -482,14 +492,19 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, _angularVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible);
|
||||
READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, _ignoreForCollisions);
|
||||
READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, _collisionsWillMove);
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << " readEntityDataFromBuffer() _registrationPoint:" << _registrationPoint;
|
||||
qDebug() << " readEntityDataFromBuffer() _visible:" << _visible;
|
||||
qDebug() << " readEntityDataFromBuffer() _ignoreForCollisions:" << _ignoreForCollisions;
|
||||
qDebug() << " readEntityDataFromBuffer() _collisionsWillMove:" << _collisionsWillMove;
|
||||
}
|
||||
|
||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
|
||||
|
||||
recalculateCollisionShape();
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
@ -675,7 +690,7 @@ void EntityItem::update(const quint64& updateTime) {
|
|||
velocity = NO_VELOCITY;
|
||||
}
|
||||
|
||||
setPosition(position);
|
||||
setPosition(position); // this will automatically recalculate our collision shape
|
||||
setVelocity(velocity);
|
||||
|
||||
if (wantDebug) {
|
||||
|
@ -731,6 +746,8 @@ EntityItemProperties EntityItem::getProperties() const {
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularDamping, getAngularDamping);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(visible, getVisible);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignoreForCollisions, getIgnoreForCollisions);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove);
|
||||
|
||||
properties._defaultSettings = false;
|
||||
|
||||
|
@ -749,7 +766,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
|
|||
}
|
||||
}
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPositionInMeters);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPositionInMeters); // this will call recalculate collision shape if needed
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setDimensionsInMeters); // NOTE: radius is obsolete
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, setMass);
|
||||
|
@ -763,6 +780,8 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, setIgnoreForCollisions);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, setCollisionsWillMove);
|
||||
|
||||
if (somethingChanged) {
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
|
@ -903,3 +922,11 @@ float EntityItem::getRadius() const {
|
|||
return radius;
|
||||
}
|
||||
|
||||
void EntityItem::recalculateCollisionShape() {
|
||||
AACube entityAACube = getMinimumAACube();
|
||||
entityAACube.scale(TREE_SCALE); // scale to meters
|
||||
_collisionShape.setTranslation(entityAACube.calcCenter());
|
||||
_collisionShape.setScale(entityAACube.getScale());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <AACubeShape.h>
|
||||
#include <AnimationCache.h> // for Animation, AnimationCache, and AnimationPointer classes
|
||||
#include <Octree.h> // for EncodeBitstreamParams class
|
||||
#include <OctreeElement.h> // for OctreeElement::AppendState
|
||||
|
@ -123,7 +124,9 @@ public:
|
|||
EntityTypes::EntityType getType() const { return _type; }
|
||||
const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0)
|
||||
glm::vec3 getPositionInMeters() const { return _position * (float) TREE_SCALE; } /// get position in meters
|
||||
void setPosition(const glm::vec3& value) { _position = value; } /// set position in domain scale units (0.0 - 1.0)
|
||||
|
||||
/// set position in domain scale units (0.0 - 1.0)
|
||||
void setPosition(const glm::vec3& value) { _position = value; recalculateCollisionShape(); }
|
||||
void setPositionInMeters(const glm::vec3& value) /// set position in meter units (0.0 - TREE_SCALE)
|
||||
{ setPosition(glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f)); }
|
||||
|
||||
|
@ -137,14 +140,14 @@ public:
|
|||
float getLargestDimension() const { return glm::length(_dimensions); } /// get the largest possible dimension
|
||||
|
||||
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
|
||||
void setDimensions(const glm::vec3& value) { _dimensions = value; }
|
||||
void setDimensions(const glm::vec3& value) { _dimensions = value; ; recalculateCollisionShape(); }
|
||||
|
||||
/// set dimensions in meter units (0.0 - TREE_SCALE) this will also reset radius appropriately
|
||||
void setDimensionsInMeters(const glm::vec3& value) { setDimensions(value / (float) TREE_SCALE); }
|
||||
|
||||
static const glm::quat DEFAULT_ROTATION;
|
||||
const glm::quat& getRotation() const { return _rotation; }
|
||||
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
||||
void setRotation(const glm::quat& rotation) { _rotation = rotation; ; recalculateCollisionShape(); }
|
||||
|
||||
static const float DEFAULT_GLOW_LEVEL;
|
||||
float getGlowLevel() const { return _glowLevel; }
|
||||
|
@ -207,7 +210,10 @@ public:
|
|||
|
||||
static const glm::vec3 DEFAULT_REGISTRATION_POINT;
|
||||
const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } /// registration point as ratio of entity
|
||||
void setRegistrationPoint(const glm::vec3& value) { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); } /// registration point as ratio of entity
|
||||
|
||||
/// registration point as ratio of entity
|
||||
void setRegistrationPoint(const glm::vec3& value)
|
||||
{ _registrationPoint = glm::clamp(value, 0.0f, 1.0f); recalculateCollisionShape(); }
|
||||
|
||||
static const glm::vec3 NO_ANGULAR_VELOCITY;
|
||||
static const glm::vec3 DEFAULT_ANGULAR_VELOCITY;
|
||||
|
@ -224,14 +230,24 @@ public:
|
|||
void setVisible(bool value) { _visible = value; }
|
||||
bool isVisible() const { return _visible; }
|
||||
bool isInvisible() const { return !_visible; }
|
||||
|
||||
static const bool DEFAULT_IGNORE_FOR_COLLISIONS;
|
||||
bool getIgnoreForCollisions() const { return _ignoreForCollisions; }
|
||||
void setIgnoreForCollisions(bool value) { _ignoreForCollisions = value; }
|
||||
|
||||
static const bool DEFAULT_COLLISIONS_WILL_MOVE;
|
||||
bool getCollisionsWillMove() const { return _collisionsWillMove; }
|
||||
void setCollisionsWillMove(bool value) { _collisionsWillMove = value; }
|
||||
|
||||
// TODO: We need to get rid of these users of getRadius()...
|
||||
float getRadius() const;
|
||||
|
||||
void applyHardCollision(const CollisionInfo& collisionInfo);
|
||||
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
|
||||
|
||||
protected:
|
||||
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
|
||||
virtual void recalculateCollisionShape();
|
||||
|
||||
EntityTypes::EntityType _type;
|
||||
QUuid _id;
|
||||
|
@ -257,6 +273,8 @@ protected:
|
|||
glm::vec3 _angularVelocity;
|
||||
float _angularDamping;
|
||||
bool _visible;
|
||||
bool _ignoreForCollisions;
|
||||
bool _collisionsWillMove;
|
||||
|
||||
// NOTE: Radius support is obsolete, but these private helper functions are available for this class to
|
||||
// parse old data streams
|
||||
|
@ -264,6 +282,7 @@ protected:
|
|||
/// set radius in domain scale units (0.0 - 1.0) this will also reset dimensions to be equal for each axis
|
||||
void setRadius(float value);
|
||||
|
||||
AACubeShape _collisionShape;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ EntityItemProperties::EntityItemProperties() :
|
|||
_angularVelocity(EntityItem::DEFAULT_ANGULAR_VELOCITY),
|
||||
_angularDamping(EntityItem::DEFAULT_ANGULAR_DAMPING),
|
||||
_visible(EntityItem::DEFAULT_VISIBLE),
|
||||
_ignoreForCollisions(EntityItem::DEFAULT_IGNORE_FOR_COLLISIONS),
|
||||
_collisionsWillMove(EntityItem::DEFAULT_COLLISIONS_WILL_MOVE),
|
||||
|
||||
_positionChanged(false),
|
||||
_dimensionsChanged(false),
|
||||
|
@ -55,6 +57,8 @@ EntityItemProperties::EntityItemProperties() :
|
|||
_angularVelocityChanged(false),
|
||||
_angularDampingChanged(false),
|
||||
_visibleChanged(false),
|
||||
_ignoreForCollisionsChanged(false),
|
||||
_collisionsWillMoveChanged(false),
|
||||
|
||||
_color(),
|
||||
_modelURL(""),
|
||||
|
@ -112,6 +116,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_REGISTRATION_POINT, registrationPoint);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping);
|
||||
CHECK_PROPERTY_CHANGE(PROP_IGNORE_FOR_COLLISIONS, ignoreForCollisions);
|
||||
CHECK_PROPERTY_CHANGE(PROP_COLLISIONS_WILL_MOVE, collisionsWillMove);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
@ -134,6 +140,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(velocity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(gravity);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(damping);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(mass);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(age, getAge()); // gettable, but not settable
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable
|
||||
|
@ -149,6 +156,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFrameIndex);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(ignoreForCollisions);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(collisionsWillMove);
|
||||
|
||||
// Sitting properties support
|
||||
QScriptValue sittingPoints = engine->newObject();
|
||||
|
@ -193,6 +202,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFrameIndex, setAnimationFrameIndex);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(glowLevel, setGlowLevel);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(ignoreForCollisions, setIgnoreForCollisions);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(collisionsWillMove, setCollisionsWillMove);
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
@ -341,6 +352,8 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, properties.getAngularVelocity());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, properties.getAngularDamping());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, properties.getVisible());
|
||||
APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, appendValue, properties.getIgnoreForCollisions());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, properties.getCollisionsWillMove());
|
||||
}
|
||||
if (propertyCount > 0) {
|
||||
int endOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||
|
@ -537,6 +550,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_FOR_COLLISIONS, bool, setIgnoreForCollisions);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONS_WILL_MOVE, bool, setCollisionsWillMove);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
|
|
@ -62,8 +62,10 @@ enum EntityPropertyList {
|
|||
PROP_REGISTRATION_POINT,
|
||||
PROP_ANGULAR_VELOCITY,
|
||||
PROP_ANGULAR_DAMPING,
|
||||
PROP_IGNORE_FOR_COLLISIONS,
|
||||
PROP_COLLISIONS_WILL_MOVE,
|
||||
|
||||
PROP_LAST_ITEM = PROP_ANGULAR_DAMPING
|
||||
PROP_LAST_ITEM = PROP_COLLISIONS_WILL_MOVE
|
||||
};
|
||||
|
||||
typedef PropertyFlags<EntityPropertyList> EntityPropertyFlags;
|
||||
|
@ -221,6 +223,12 @@ public:
|
|||
bool getVisible() const { return _visible; }
|
||||
void setVisible(bool value) { _visible = value; _visibleChanged = true; }
|
||||
|
||||
bool getIgnoreForCollisions() const { return _ignoreForCollisions; }
|
||||
void setIgnoreForCollisions(bool value) { _ignoreForCollisions = value; _ignoreForCollisionsChanged = true; }
|
||||
|
||||
bool getCollisionsWillMove() const { return _collisionsWillMove; }
|
||||
void setCollisionsWillMove(bool value) { _collisionsWillMove = value; _collisionsWillMoveChanged = true; }
|
||||
|
||||
void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; }
|
||||
|
||||
private:
|
||||
|
@ -247,6 +255,8 @@ private:
|
|||
glm::vec3 _angularVelocity;
|
||||
float _angularDamping;
|
||||
bool _visible;
|
||||
bool _ignoreForCollisions;
|
||||
bool _collisionsWillMove;
|
||||
|
||||
bool _positionChanged;
|
||||
bool _dimensionsChanged;
|
||||
|
@ -261,6 +271,8 @@ private:
|
|||
bool _angularVelocityChanged;
|
||||
bool _angularDampingChanged;
|
||||
bool _visibleChanged;
|
||||
bool _ignoreForCollisionsChanged;
|
||||
bool _collisionsWillMoveChanged;
|
||||
|
||||
// TODO: this need to be more generic. for now, we're going to have the properties class support these as
|
||||
// named getter/setters, but we want to move them to generic types...
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <AACubeShape.h>
|
||||
#include <ShapeCollider.h>
|
||||
|
||||
#include <FBXReader.h>
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
|
@ -546,6 +549,27 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad
|
|||
return false;
|
||||
}
|
||||
|
||||
bool EntityTreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const {
|
||||
bool atLeastOneCollision = false;
|
||||
QList<EntityItem*>::iterator entityItr = _entityItems->begin();
|
||||
QList<EntityItem*>::const_iterator entityEnd = _entityItems->end();
|
||||
while(entityItr != entityEnd) {
|
||||
EntityItem* entity = (*entityItr);
|
||||
|
||||
// entities that are set for ignore for collisions then don't consider them for collision
|
||||
const Shape* otherCollisionShape = &entity->getCollisionShapeInMeters();
|
||||
if (shape != otherCollisionShape && !entity->getIgnoreForCollisions()) {
|
||||
if (ShapeCollider::collideShapes(shape, otherCollisionShape, collisions)) {
|
||||
CollisionInfo* lastCollision = collisions.getLastCollision();
|
||||
lastCollision->_extraData = entity;
|
||||
atLeastOneCollision = true;
|
||||
}
|
||||
}
|
||||
++entityItr;
|
||||
}
|
||||
return atLeastOneCollision;
|
||||
}
|
||||
|
||||
void EntityTreeElement::updateEntityItemID(const EntityItemID& creatorTokenEntityID, const EntityItemID& knownIDEntityID) {
|
||||
uint16_t numberOfEntities = _entityItems->size();
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
|
|
|
@ -142,6 +142,8 @@ public:
|
|||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const;
|
||||
|
||||
virtual bool findShapeCollisions(const Shape* shape, CollisionList& collisions) const;
|
||||
|
||||
const QList<EntityItem*>& getEntities() const { return *_entityItems; }
|
||||
QList<EntityItem*>& getEntities() { return *_entityItems; }
|
||||
bool hasEntities() const { return _entityItems ? _entityItems->size() > 0 : false; }
|
||||
|
|
|
@ -88,4 +88,11 @@ void SphereEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBi
|
|||
|
||||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
|
||||
}
|
||||
}
|
||||
|
||||
void SphereEntityItem::recalculateCollisionShape() {
|
||||
_sphereShape.setTranslation(getCenterInMeters());
|
||||
glm::vec3 dimensionsInMeters = getDimensionsInMeters();
|
||||
float largestDiameter = glm::max(dimensionsInMeters.x, dimensionsInMeters.y, dimensionsInMeters.z);
|
||||
_sphereShape.setRadius(largestDiameter / 2.0f);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
#ifndef hifi_SphereEntityItem_h
|
||||
#define hifi_SphereEntityItem_h
|
||||
|
||||
#include "EntityItem.h"
|
||||
#include <SphereShape.h>
|
||||
#include "EntityItem.h"
|
||||
|
||||
class SphereEntityItem : public EntityItem {
|
||||
public:
|
||||
|
@ -49,9 +50,14 @@ public:
|
|||
_color[GREEN_INDEX] = value.green;
|
||||
_color[BLUE_INDEX] = value.blue;
|
||||
}
|
||||
|
||||
virtual const Shape& getCollisionShapeInMeters() const { return _sphereShape; }
|
||||
|
||||
protected:
|
||||
virtual void recalculateCollisionShape();
|
||||
|
||||
rgbColor _color;
|
||||
SphereShape _sphereShape;
|
||||
};
|
||||
|
||||
#endif // hifi_SphereEntityItem_h
|
||||
|
|
|
@ -80,6 +80,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
|||
|
||||
// if this is a relative path then handle it as a relative viewpoint
|
||||
handleRelativeViewpoint(lookupUrl.path());
|
||||
emit lookupResultsFinished();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -149,6 +150,7 @@ void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) {
|
|||
// we've been told that this result exists but is offline, emit our signal so the application can handle
|
||||
emit lookupResultIsOffline();
|
||||
}
|
||||
emit lookupResultsFinished();
|
||||
}
|
||||
|
||||
void AddressManager::handleAPIError(QNetworkReply& errorReply) {
|
||||
|
@ -157,6 +159,7 @@ void AddressManager::handleAPIError(QNetworkReply& errorReply) {
|
|||
if (errorReply.error() == QNetworkReply::ContentNotFoundError) {
|
||||
emit lookupResultIsNotFound();
|
||||
}
|
||||
emit lookupResultsFinished();
|
||||
}
|
||||
|
||||
const QString GET_PLACE = "/api/v1/places/%1";
|
||||
|
@ -164,7 +167,7 @@ const QString GET_PLACE = "/api/v1/places/%1";
|
|||
void AddressManager::attemptPlaceNameLookup(const QString& lookupString) {
|
||||
// assume this is a place name and see if we can get any info on it
|
||||
QString placeName = QUrl::toPercentEncoding(lookupString);
|
||||
AccountManager::getInstance().authenticatedRequest(GET_PLACE.arg(placeName),
|
||||
AccountManager::getInstance().unauthenticatedRequest(GET_PLACE.arg(placeName),
|
||||
QNetworkAccessManager::GetOperation,
|
||||
apiCallbackParameters());
|
||||
}
|
||||
|
@ -180,6 +183,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
|||
|
||||
if (hostnameRegex.indexIn(lookupString) != -1) {
|
||||
emit possibleDomainChangeRequired(hostnameRegex.cap(0));
|
||||
emit lookupResultsFinished();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -187,6 +191,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
|||
|
||||
if (ipAddressRegex.indexIn(lookupString) != -1) {
|
||||
emit possibleDomainChangeRequired(ipAddressRegex.cap(0));
|
||||
emit lookupResultsFinished();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -263,7 +268,7 @@ bool AddressManager::handleUsername(const QString& lookupString) {
|
|||
void AddressManager::goToUser(const QString& username) {
|
||||
QString formattedUsername = QUrl::toPercentEncoding(username);
|
||||
// this is a username - pull the captured name and lookup that user's location
|
||||
AccountManager::getInstance().authenticatedRequest(GET_USER_LOCATION.arg(formattedUsername),
|
||||
AccountManager::getInstance().unauthenticatedRequest(GET_USER_LOCATION.arg(formattedUsername),
|
||||
QNetworkAccessManager::GetOperation,
|
||||
apiCallbackParameters());
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ public slots:
|
|||
void handleAPIError(QNetworkReply& errorReply);
|
||||
void goToUser(const QString& username);
|
||||
signals:
|
||||
void lookupResultsFinished();
|
||||
void lookupResultIsOffline();
|
||||
void lookupResultIsNotFound();
|
||||
void possibleDomainChangeRequired(const QString& newHostname);
|
||||
|
|
|
@ -817,9 +817,6 @@ bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) {
|
|||
if (!box.expandedIntersectsSegment(args->start, args->end, args->radius)) {
|
||||
return false;
|
||||
}
|
||||
if (!element->isLeaf()) {
|
||||
return true; // recurse on children
|
||||
}
|
||||
if (element->hasContent()) {
|
||||
glm::vec3 nodePenetration;
|
||||
if (box.findCapsulePenetration(args->start, args->end, args->radius, nodePenetration)) {
|
||||
|
@ -827,27 +824,29 @@ bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) {
|
|||
args->found = true;
|
||||
}
|
||||
}
|
||||
if (!element->isLeaf()) {
|
||||
return true; // recurse on children
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool findShapeCollisionsOp(OctreeElement* element, void* extraData) {
|
||||
ShapeArgs* args = static_cast<ShapeArgs*>(extraData);
|
||||
|
||||
// coarse check against bounds
|
||||
AACube cube = element->getAACube();
|
||||
cube.scale(TREE_SCALE);
|
||||
if (!cube.expandedContains(args->shape->getTranslation(), args->shape->getBoundingRadius())) {
|
||||
return false;
|
||||
}
|
||||
if (!element->isLeaf()) {
|
||||
return true; // recurse on children
|
||||
}
|
||||
if (element->hasContent()) {
|
||||
if (ShapeCollider::collideShapeWithAACubeLegacy(args->shape, cube.calcCenter(), cube.getScale(), args->collisions)) {
|
||||
if (element->findShapeCollisions(args->shape, args->collisions)) {
|
||||
args->found = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!element->isLeaf()) {
|
||||
return true; // recurse on children
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
@ -17,7 +18,8 @@
|
|||
|
||||
#include <NodeList.h>
|
||||
#include <PerfStat.h>
|
||||
#include <assert.h>
|
||||
#include <AACubeShape.h>
|
||||
#include <ShapeCollider.h>
|
||||
|
||||
#include "AACube.h"
|
||||
#include "OctalCode.h"
|
||||
|
@ -1369,6 +1371,11 @@ bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius,
|
|||
return _cube.findSpherePenetration(center, radius, penetration);
|
||||
}
|
||||
|
||||
bool OctreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const {
|
||||
AACube cube = getAACube();
|
||||
cube.scale(TREE_SCALE);
|
||||
return ShapeCollider::collideShapeWithAACubeLegacy(shape, cube.calcCenter(), cube.getScale(), collisions);
|
||||
}
|
||||
|
||||
// TODO: consider removing this, or switching to using getOrCreateChildElementContaining(const AACube& box)...
|
||||
OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float z, float s) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "ViewFrustum.h"
|
||||
#include "OctreeConstants.h"
|
||||
|
||||
class CollisionList;
|
||||
class EncodeBitstreamParams;
|
||||
class Octree;
|
||||
class OctreeElement;
|
||||
|
@ -32,6 +33,7 @@ class OctreeElementBag;
|
|||
class OctreeElementDeleteHook;
|
||||
class OctreePacketData;
|
||||
class ReadBitstreamToTreeParams;
|
||||
class Shape;
|
||||
class VoxelSystem;
|
||||
|
||||
const float SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE = (1.0f / TREE_SCALE) / 10000.0f; // 1/10,000th of a meter
|
||||
|
@ -128,6 +130,8 @@ public:
|
|||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const;
|
||||
|
||||
virtual bool findShapeCollisions(const Shape* shape, CollisionList& collisions) const;
|
||||
|
||||
// Base class methods you don't need to implement
|
||||
const unsigned char* getOctalCode() const { return (_octcodePointer) ? _octalCode.pointer : &_octalCode.buffer[0]; }
|
||||
OctreeElement* getChildAtIndex(int childIndex) const;
|
||||
|
|
|
@ -48,7 +48,8 @@ EntityScriptingInterface ScriptEngine::_entityScriptingInterface;
|
|||
|
||||
static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* engine) {
|
||||
QUrl soundURL = QUrl(context->argument(0).toString());
|
||||
QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL), QScriptEngine::ScriptOwnership);
|
||||
bool isStereo = context->argument(1).toBool();
|
||||
QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL, isStereo), QScriptEngine::ScriptOwnership);
|
||||
|
||||
return soundScriptValue;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#ifndef hifi_AACubeShape_h
|
||||
#define hifi_AACubeShape_h
|
||||
|
||||
#include <QDebug>
|
||||
#include "Shape.h"
|
||||
|
||||
class AACubeShape : public Shape {
|
||||
|
@ -28,9 +29,22 @@ public:
|
|||
bool findRayIntersection(RayIntersectionInfo& intersection) const;
|
||||
|
||||
float getVolume() const { return _scale * _scale * _scale; }
|
||||
virtual QDebug& dumpToDebug(QDebug& debugConext) const;
|
||||
|
||||
protected:
|
||||
float _scale;
|
||||
};
|
||||
|
||||
inline QDebug& AACubeShape::dumpToDebug(QDebug& debugConext) const {
|
||||
debugConext << "AACubeShape[ ("
|
||||
<< "type: " << getType()
|
||||
<< "position: "
|
||||
<< getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z
|
||||
<< "scale: "
|
||||
<< getScale()
|
||||
<< "]";
|
||||
|
||||
return debugConext;
|
||||
}
|
||||
|
||||
#endif // hifi_AACubeShape_h
|
||||
|
|
|
@ -54,6 +54,8 @@ public:
|
|||
const Shape* _shapeA; // pointer to shapeA in this collision
|
||||
const Shape* _shapeB; // pointer to shapeB in this collision
|
||||
|
||||
void* _extraData; // pointer to extraData for this collision, opaque to the collision info, useful for external data
|
||||
|
||||
float _damping; // range [0,1] of friction coeficient
|
||||
float _elasticity; // range [0,1] of energy conservation
|
||||
glm::vec3 _contactPoint; // world-frame point on BodyA that is deepest into BodyB
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
#include <QVector>
|
||||
|
||||
|
@ -80,6 +81,8 @@ public:
|
|||
virtual float getVolume() const { return 1.0; }
|
||||
|
||||
virtual void getVerletPoints(QVector<VerletPoint*>& points) {}
|
||||
|
||||
virtual QDebug& dumpToDebug(QDebug& debugConext) const;
|
||||
|
||||
protected:
|
||||
// these ctors are protected (used by derived classes only)
|
||||
|
@ -113,4 +116,25 @@ protected:
|
|||
float _mass;
|
||||
};
|
||||
|
||||
inline QDebug& Shape::dumpToDebug(QDebug& debugConext) const {
|
||||
debugConext << "Shape[ ("
|
||||
<< "type: " << getType()
|
||||
<< "position: "
|
||||
<< getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z
|
||||
<< "radius: "
|
||||
<< getBoundingRadius()
|
||||
<< "]";
|
||||
|
||||
return debugConext;
|
||||
}
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const Shape& shape) {
|
||||
return shape.dumpToDebug(debug);
|
||||
}
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const Shape* shape) {
|
||||
return shape->dumpToDebug(debug);
|
||||
}
|
||||
|
||||
|
||||
#endif // hifi_Shape_h
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "PlaneShape.h"
|
||||
#include "SphereShape.h"
|
||||
|
||||
#include "StreamUtils.h"
|
||||
|
||||
// NOTE:
|
||||
//
|
||||
// * Large ListShape's are inefficient keep the lists short.
|
||||
|
@ -978,7 +980,112 @@ bool aaCubeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& co
|
|||
return capsuleVsAACube(shapeB, shapeA, collisions);
|
||||
}
|
||||
|
||||
// helper function
|
||||
CollisionInfo* aaCubeVsAACubeHelper(const glm::vec3& cubeCenterA, float cubeSideA, const glm::vec3& cubeCenterB,
|
||||
float cubeSideB, CollisionList& collisions) {
|
||||
// cube is A
|
||||
// cube is B
|
||||
// BA = B - A = from center of A to center of B
|
||||
float halfCubeSideA = 0.5f * cubeSideA;
|
||||
float halfCubeSideB = 0.5f * cubeSideB;
|
||||
glm::vec3 BA = cubeCenterB - cubeCenterA;
|
||||
|
||||
float distance = glm::length(BA);
|
||||
|
||||
if (distance > EPSILON) {
|
||||
float maxBA = glm::max(glm::max(glm::abs(BA.x), glm::abs(BA.y)), glm::abs(BA.z));
|
||||
if (maxBA > halfCubeSideB + halfCubeSideA) {
|
||||
// cube misses cube entirely
|
||||
return NULL;
|
||||
}
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (!collision) {
|
||||
return NULL; // no more room for collisions
|
||||
}
|
||||
if (maxBA > halfCubeSideB) {
|
||||
// cube hits cube but its center is outside cube
|
||||
// compute contact anti-pole on cube (in cube frame)
|
||||
glm::vec3 cubeContact = glm::abs(BA);
|
||||
if (cubeContact.x > halfCubeSideB) {
|
||||
cubeContact.x = halfCubeSideB;
|
||||
}
|
||||
if (cubeContact.y > halfCubeSideB) {
|
||||
cubeContact.y = halfCubeSideB;
|
||||
}
|
||||
if (cubeContact.z > halfCubeSideB) {
|
||||
cubeContact.z = halfCubeSideB;
|
||||
}
|
||||
glm::vec3 signs = glm::sign(BA);
|
||||
cubeContact.x *= signs.x;
|
||||
cubeContact.y *= signs.y;
|
||||
cubeContact.z *= signs.z;
|
||||
|
||||
// compute penetration direction
|
||||
glm::vec3 direction = BA - cubeContact;
|
||||
|
||||
float lengthDirection = glm::length(direction);
|
||||
|
||||
if (lengthDirection < EPSILON) {
|
||||
// cubeCenterA is touching cube B surface, so we can't use the difference between those two
|
||||
// points to compute the penetration direction. Instead we use the unitary components of
|
||||
// cubeContact.
|
||||
glm::modf(cubeContact / halfCubeSideB, direction);
|
||||
lengthDirection = glm::length(direction);
|
||||
} else if (lengthDirection > halfCubeSideA) {
|
||||
collisions.deleteLastCollision();
|
||||
return NULL;
|
||||
}
|
||||
direction /= lengthDirection;
|
||||
|
||||
// compute collision details
|
||||
collision->_contactPoint = cubeCenterA + halfCubeSideA * direction;
|
||||
collision->_penetration = halfCubeSideA * direction - (BA - cubeContact);
|
||||
} else {
|
||||
// cube center is inside cube
|
||||
// --> push out nearest face
|
||||
glm::vec3 direction;
|
||||
BA /= maxBA;
|
||||
glm::modf(BA, direction);
|
||||
float lengthDirection = glm::length(direction);
|
||||
direction /= lengthDirection;
|
||||
|
||||
// compute collision details
|
||||
collision->_floatData = cubeSideB;
|
||||
collision->_vecData = cubeCenterB;
|
||||
collision->_penetration = (halfCubeSideB * lengthDirection + halfCubeSideA - maxBA * glm::dot(BA, direction)) * direction;
|
||||
collision->_contactPoint = cubeCenterA + halfCubeSideA * direction;
|
||||
}
|
||||
collision->_shapeA = NULL;
|
||||
collision->_shapeB = NULL;
|
||||
return collision;
|
||||
} else if (halfCubeSideA + halfCubeSideB > distance) {
|
||||
// NOTE: for cocentric approximation we collide sphere and cube as two spheres which means
|
||||
// this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON)
|
||||
CollisionInfo* collision = collisions.getNewCollision();
|
||||
if (collision) {
|
||||
// the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis)
|
||||
collision->_penetration = (halfCubeSideA + halfCubeSideB) * glm::vec3(0.0f, -1.0f, 0.0f);
|
||||
// contactPoint is on surface of A
|
||||
collision->_contactPoint = cubeCenterA + collision->_penetration;
|
||||
collision->_shapeA = NULL;
|
||||
collision->_shapeB = NULL;
|
||||
return collision;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool aaCubeVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) {
|
||||
// BA = B - A = from center of A to center of B
|
||||
const AACubeShape* cubeA = static_cast<const AACubeShape*>(shapeA);
|
||||
const AACubeShape* cubeB = static_cast<const AACubeShape*>(shapeB);
|
||||
CollisionInfo* collision = aaCubeVsAACubeHelper( cubeA->getTranslation(), cubeA->getScale(),
|
||||
cubeB->getTranslation(), cubeB->getScale(), collisions);
|
||||
if (collision) {
|
||||
collision->_shapeA = shapeA;
|
||||
collision->_shapeB = shapeB;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,4 +39,26 @@ public:
|
|||
float getVolume() const { return 1.3333333333f * PI * _boundingRadius * _boundingRadius * _boundingRadius; }
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const SphereShape& shape) {
|
||||
debug << "SphereShape[ ("
|
||||
<< "position: "
|
||||
<< shape.getTranslation().x << ", " << shape.getTranslation().y << ", " << shape.getTranslation().z
|
||||
<< "radius: "
|
||||
<< shape.getRadius()
|
||||
<< "]";
|
||||
|
||||
return debug;
|
||||
}
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const SphereShape* shape) {
|
||||
debug << "SphereShape[ ("
|
||||
<< "center: "
|
||||
<< shape->getTranslation().x << ", " << shape->getTranslation().y << ", " << shape->getTranslation().z
|
||||
<< "radius: "
|
||||
<< shape->getRadius()
|
||||
<< "]";
|
||||
|
||||
return debug;
|
||||
}
|
||||
|
||||
#endif // hifi_SphereShape_h
|
||||
|
|
Loading…
Reference in a new issue