mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-16 22:30:42 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
This commit is contained in:
commit
07a6fa56e0
30 changed files with 539 additions and 358 deletions
|
@ -23,7 +23,8 @@
|
|||
|
||||
MetavoxelServer::MetavoxelServer(const QByteArray& packet) :
|
||||
ThreadedAssignment(packet),
|
||||
_nextSender(0) {
|
||||
_nextSender(0),
|
||||
_savedDataInitialized(false) {
|
||||
}
|
||||
|
||||
void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) {
|
||||
|
@ -33,8 +34,20 @@ void MetavoxelServer::applyEdit(const MetavoxelEditMessage& edit) {
|
|||
}
|
||||
|
||||
void MetavoxelServer::setData(const MetavoxelData& data) {
|
||||
if (_data != data) {
|
||||
emit dataChanged(_data = data);
|
||||
if (_data == data) {
|
||||
return;
|
||||
}
|
||||
emit dataChanged(_data = data);
|
||||
|
||||
if (!_savedDataInitialized) {
|
||||
_savedData = data;
|
||||
_savedDataInitialized = true;
|
||||
|
||||
// start the save timer
|
||||
QTimer* saveTimer = new QTimer(this);
|
||||
connect(saveTimer, &QTimer::timeout, this, &MetavoxelServer::maybeSaveData);
|
||||
const int SAVE_INTERVAL = 1000 * 30;
|
||||
saveTimer->start(SAVE_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,6 +148,12 @@ void MetavoxelServer::maybeDeleteSession(const SharedNodePointer& node) {
|
|||
}
|
||||
}
|
||||
|
||||
void MetavoxelServer::maybeSaveData() {
|
||||
if (_savedData != _data) {
|
||||
QMetaObject::invokeMethod(_persister, "save", Q_ARG(const MetavoxelData&, _savedData = _data));
|
||||
}
|
||||
}
|
||||
|
||||
MetavoxelSender::MetavoxelSender(MetavoxelServer* server) :
|
||||
_server(server),
|
||||
_sendTimer(this) {
|
||||
|
|
|
@ -52,6 +52,7 @@ private slots:
|
|||
|
||||
void maybeAttachSession(const SharedNodePointer& node);
|
||||
void maybeDeleteSession(const SharedNodePointer& node);
|
||||
void maybeSaveData();
|
||||
|
||||
private:
|
||||
|
||||
|
@ -61,6 +62,8 @@ private:
|
|||
MetavoxelPersister* _persister;
|
||||
|
||||
MetavoxelData _data;
|
||||
MetavoxelData _savedData;
|
||||
bool _savedDataInitialized;
|
||||
};
|
||||
|
||||
/// Handles update sending for one thread.
|
||||
|
|
|
@ -589,8 +589,6 @@ void Application::paintGL() {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::paintGL()");
|
||||
|
||||
const bool glowEnabled = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect);
|
||||
|
||||
// Set the desired FBO texture size. If it hasn't changed, this does nothing.
|
||||
// Otherwise, it must rebuild the FBOs
|
||||
if (OculusManager::isConnected()) {
|
||||
|
@ -665,16 +663,11 @@ void Application::paintGL() {
|
|||
updateShadowMap();
|
||||
}
|
||||
|
||||
//If we aren't using the glow shader, we have to clear the color and depth buffer
|
||||
if (!glowEnabled) {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
} else if (OculusManager::isConnected()) {
|
||||
if (OculusManager::isConnected()) {
|
||||
//Clear the color buffer to ensure that there isnt any residual color
|
||||
//Left over from when OR was not connected.
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
if (OculusManager::isConnected()) {
|
||||
|
||||
//When in mirror mode, use camera rotation. Otherwise, use body rotation
|
||||
if (whichCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
OculusManager::display(whichCamera.getRotation(), whichCamera.getPosition(), whichCamera);
|
||||
|
@ -687,9 +680,7 @@ void Application::paintGL() {
|
|||
TV3DManager::display(whichCamera);
|
||||
|
||||
} else {
|
||||
if (glowEnabled) {
|
||||
_glowEffect.prepare();
|
||||
}
|
||||
_glowEffect.prepare();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
|
@ -697,9 +688,7 @@ void Application::paintGL() {
|
|||
displaySide(whichCamera);
|
||||
glPopMatrix();
|
||||
|
||||
if (glowEnabled) {
|
||||
_glowEffect.render();
|
||||
}
|
||||
_glowEffect.render();
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
renderRearViewMirror(_mirrorViewRect);
|
||||
|
@ -2809,6 +2798,10 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
_stars.render(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(), whichCamera.getNearClip(), alpha);
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Wireframe)) {
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
}
|
||||
|
||||
// draw the sky dome
|
||||
if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) {
|
||||
PerformanceTimer perfTimer("atmosphere");
|
||||
|
@ -2947,6 +2940,10 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
_overlays.render3D();
|
||||
}
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Wireframe)) {
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation) {
|
||||
|
|
|
@ -370,7 +370,7 @@ Menu::Menu() :
|
|||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::AvatarsReceiveShadows, 0, true));
|
||||
addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::AvatarsReceiveShadows, 0, true);
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
|
||||
|
@ -386,6 +386,7 @@ Menu::Menu() :
|
|||
0,
|
||||
appInstance->getGlowEffect(),
|
||||
SLOT(cycleRenderMode()));
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, 0, false);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));
|
||||
|
||||
QMenu* avatarDebugMenu = developerMenu->addMenu("Avatar");
|
||||
|
|
|
@ -471,6 +471,7 @@ namespace MenuOption {
|
|||
const QString Voxels = "Voxels";
|
||||
const QString VoxelTextures = "Voxel Textures";
|
||||
const QString WalletPrivateKey = "Wallet Private Key...";
|
||||
const QString Wireframe = "Wireframe";
|
||||
}
|
||||
|
||||
void sendFakeEnterEvent();
|
||||
|
|
|
@ -50,12 +50,13 @@ Avatar::Avatar() :
|
|||
AvatarData(),
|
||||
_skeletonModel(this),
|
||||
_bodyYawDelta(0.0f),
|
||||
_velocity(0.0f, 0.0f, 0.0f),
|
||||
_lastVelocity(0.0f, 0.0f, 0.0f),
|
||||
_acceleration(0.0f, 0.0f, 0.0f),
|
||||
_angularVelocity(0.0f, 0.0f, 0.0f),
|
||||
_lastAngularVelocity(0.0f, 0.0f, 0.0f),
|
||||
_angularAcceleration(0.0f, 0.0f, 0.0f),
|
||||
_lastPosition(0.0f),
|
||||
_velocity(0.0f),
|
||||
_lastVelocity(0.0f),
|
||||
_acceleration(0.0f),
|
||||
_angularVelocity(0.0f),
|
||||
_lastAngularVelocity(0.0f),
|
||||
_angularAcceleration(0.0f),
|
||||
_lastOrientation(),
|
||||
_leanScale(0.5f),
|
||||
_scale(1.0f),
|
||||
|
@ -185,11 +186,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
_hair.simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
// update position by velocity, and subtract the change added earlier for gravity
|
||||
_position += _velocity * deltaTime;
|
||||
updateAcceleration(deltaTime);
|
||||
|
||||
|
||||
// update animation for display name fade in/out
|
||||
if ( _displayNameTargetAlpha != _displayNameAlpha) {
|
||||
// the alpha function is
|
||||
|
@ -206,17 +203,23 @@ void Avatar::simulate(float deltaTime) {
|
|||
}
|
||||
_displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha;
|
||||
}
|
||||
|
||||
_position += _velocity * deltaTime;
|
||||
measureMotionDerivatives(deltaTime);
|
||||
}
|
||||
|
||||
void Avatar::updateAcceleration(float deltaTime) {
|
||||
// Linear Component of Acceleration
|
||||
_acceleration = (_velocity - _lastVelocity) * (1.f / deltaTime);
|
||||
void Avatar::measureMotionDerivatives(float deltaTime) {
|
||||
// linear
|
||||
float invDeltaTime = 1.0f / deltaTime;
|
||||
_velocity = (_position - _lastPosition) * invDeltaTime;
|
||||
_lastPosition = _position;
|
||||
_acceleration = (_velocity - _lastVelocity) * invDeltaTime;
|
||||
_lastVelocity = _velocity;
|
||||
// Angular Component of Acceleration
|
||||
// angular
|
||||
glm::quat orientation = getOrientation();
|
||||
glm::quat delta = glm::inverse(_lastOrientation) * orientation;
|
||||
_angularVelocity = safeEulerAngles(delta) * (1.f / deltaTime);
|
||||
_angularAcceleration = (_angularVelocity - _lastAngularVelocity) * (1.f / deltaTime);
|
||||
_angularVelocity = safeEulerAngles(delta) * invDeltaTime;
|
||||
_angularAcceleration = (_angularVelocity - _lastAngularVelocity) * invDeltaTime;
|
||||
_lastOrientation = getOrientation();
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,6 @@ public:
|
|||
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
|
||||
glm::vec3 getChestPosition() const;
|
||||
float getScale() const { return _scale; }
|
||||
Q_INVOKABLE const glm::vec3& getVelocity() const { return _velocity; }
|
||||
const Head* getHead() const { return static_cast<const Head*>(_headData); }
|
||||
Head* getHead() { return static_cast<Head*>(_headData); }
|
||||
Hand* getHand() { return static_cast<Hand*>(_handData); }
|
||||
|
@ -152,6 +151,7 @@ public:
|
|||
Q_INVOKABLE glm::quat getJointCombinedRotation(int index) const;
|
||||
Q_INVOKABLE glm::quat getJointCombinedRotation(const QString& name) const;
|
||||
|
||||
Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
|
||||
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
|
||||
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
||||
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
||||
|
@ -172,6 +172,12 @@ protected:
|
|||
SkeletonModel _skeletonModel;
|
||||
QVector<Model*> _attachmentModels;
|
||||
float _bodyYawDelta;
|
||||
|
||||
// These position histories and derivatives are in the world-frame.
|
||||
// The derivatives are the MEASURED results of all external and internal forces
|
||||
// and are therefor READ-ONLY --> motion control of the Avatar is NOT obtained
|
||||
// by setting these values.
|
||||
glm::vec3 _lastPosition;
|
||||
glm::vec3 _velocity;
|
||||
glm::vec3 _lastVelocity;
|
||||
glm::vec3 _acceleration;
|
||||
|
@ -179,6 +185,7 @@ protected:
|
|||
glm::vec3 _lastAngularVelocity;
|
||||
glm::vec3 _angularAcceleration;
|
||||
glm::quat _lastOrientation;
|
||||
|
||||
float _leanScale;
|
||||
float _scale;
|
||||
glm::vec3 _worldUpDirection;
|
||||
|
@ -195,7 +202,7 @@ protected:
|
|||
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void setScale(float scale);
|
||||
void updateAcceleration(float deltaTime);
|
||||
void measureMotionDerivatives(float deltaTime);
|
||||
|
||||
float getSkeletonHeight() const;
|
||||
float getHeadHeight() const;
|
||||
|
|
|
@ -62,9 +62,9 @@ MyAvatar::MyAvatar() :
|
|||
_mousePressed(false),
|
||||
_bodyPitchDelta(0.0f),
|
||||
_bodyRollDelta(0.0f),
|
||||
_shouldJump(false),
|
||||
_gravity(0.0f, 0.0f, 0.0f),
|
||||
_distanceToNearestAvatar(std::numeric_limits<float>::max()),
|
||||
_shouldJump(false),
|
||||
_wasPushing(false),
|
||||
_isPushing(false),
|
||||
_isBraking(false),
|
||||
|
@ -74,7 +74,6 @@ MyAvatar::MyAvatar() :
|
|||
_motorTimescale(DEFAULT_MOTOR_TIMESCALE),
|
||||
_maxMotorSpeed(MAX_MOTOR_SPEED),
|
||||
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
|
||||
_lastBodyPenetration(0.0f),
|
||||
_lastFloorContactPoint(0.0f),
|
||||
_lookAtTargetAvatar(),
|
||||
_shouldRender(true),
|
||||
|
@ -103,7 +102,7 @@ void MyAvatar::reset() {
|
|||
_skeletonModel.reset();
|
||||
getHead()->reset();
|
||||
|
||||
setVelocity(glm::vec3(0.0f));
|
||||
_velocity = glm::vec3(0.0f);
|
||||
setThrust(glm::vec3(0.0f));
|
||||
// Reset the pitch and roll components of the avatar's orientation, preserve yaw direction
|
||||
glm::vec3 eulers = safeEulerAngles(getOrientation());
|
||||
|
@ -1159,6 +1158,11 @@ void MyAvatar::updatePosition(float deltaTime) {
|
|||
boundingShape.getStartPoint(startCap);
|
||||
glm::vec3 bottom = startCap - boundingShape.getRadius() * _worldUpDirection;
|
||||
|
||||
// velocity is initialized to the measured _velocity but will be modified
|
||||
// by friction, external thrust, etc
|
||||
glm::vec3 velocity = _velocity;
|
||||
|
||||
// apply friction
|
||||
if (gravityLength > EPSILON) {
|
||||
float speedFromGravity = _scale * deltaTime * gravityLength;
|
||||
float distanceToFall = glm::distance(bottom, _lastFloorContactPoint);
|
||||
|
@ -1167,26 +1171,26 @@ void MyAvatar::updatePosition(float deltaTime) {
|
|||
if (walkingOnFloor) {
|
||||
// BEGIN HACK: to prevent the avatar from bouncing on a floor surface
|
||||
if (distanceToFall < deltaTime * speedFromGravity) {
|
||||
float verticalSpeed = glm::dot(_velocity, _worldUpDirection);
|
||||
float verticalSpeed = glm::dot(velocity, _worldUpDirection);
|
||||
if (fabs(verticalSpeed) < speedFromGravity) {
|
||||
// we're standing on a floor, and nearly at rest so we zero the vertical velocity component
|
||||
_velocity -= verticalSpeed * _worldUpDirection;
|
||||
velocity -= verticalSpeed * _worldUpDirection;
|
||||
}
|
||||
} else {
|
||||
// fall with gravity against floor
|
||||
_velocity -= speedFromGravity * _worldUpDirection;
|
||||
velocity -= speedFromGravity * _worldUpDirection;
|
||||
}
|
||||
// END HACK
|
||||
} else {
|
||||
if (!_isBraking) {
|
||||
// fall with gravity toward floor
|
||||
_velocity -= speedFromGravity * _worldUpDirection;
|
||||
velocity -= speedFromGravity * _worldUpDirection;
|
||||
}
|
||||
|
||||
if (_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) {
|
||||
const float MAX_VERTICAL_FLOOR_DETECTION_SPEED = _scale * MAX_WALKING_SPEED;
|
||||
if (keyboardInput && glm::dot(_motorVelocity, _worldUpDirection) > 0.0f &&
|
||||
glm::dot(_velocity, _worldUpDirection) > MAX_VERTICAL_FLOOR_DETECTION_SPEED) {
|
||||
glm::dot(velocity, _worldUpDirection) > MAX_VERTICAL_FLOOR_DETECTION_SPEED) {
|
||||
// disable local gravity when flying up
|
||||
setLocalGravity(glm::vec3(0.0f));
|
||||
} else {
|
||||
|
@ -1198,93 +1202,123 @@ void MyAvatar::updatePosition(float deltaTime) {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((_collisionGroups & COLLISION_GROUP_VOXELS) &&
|
||||
_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) {
|
||||
const float MIN_FLOOR_DETECTION_SPEED = _scale * 1.0f;
|
||||
if (glm::length(_velocity) < MIN_FLOOR_DETECTION_SPEED ) {
|
||||
// scan for floor under avatar
|
||||
const float maxFloorDistance = _scale * NEARBY_FLOOR_THRESHOLD;
|
||||
if (computeDistanceToFloor(bottom) < maxFloorDistance) {
|
||||
// enable local gravity
|
||||
setLocalGravity(-_worldUpDirection);
|
||||
}
|
||||
} else if ((_collisionGroups & COLLISION_GROUP_VOXELS) &&
|
||||
_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) {
|
||||
const float MIN_FLOOR_DETECTION_SPEED = _scale * 1.0f;
|
||||
if (glm::length(_velocity) < MIN_FLOOR_DETECTION_SPEED ) {
|
||||
// scan for floor under avatar
|
||||
const float maxFloorDistance = _scale * NEARBY_FLOOR_THRESHOLD;
|
||||
if (computeDistanceToFloor(bottom) < maxFloorDistance) {
|
||||
// enable local gravity
|
||||
setLocalGravity(-_worldUpDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keyboardInput > 0.0f || glm::length2(_velocity) > 0.0f || glm::length2(_thrust) > 0.0f || ! walkingOnFloor) {
|
||||
// update motor and thrust
|
||||
updateMotorFromKeyboard(deltaTime, walkingOnFloor);
|
||||
applyMotor(deltaTime);
|
||||
applyThrust(deltaTime);
|
||||
float speed = glm::length(velocity);
|
||||
if (keyboardInput > 0.0f || speed > 0.0f || glm::length2(_thrust) > 0.0f || ! walkingOnFloor) {
|
||||
// update motor
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED) {
|
||||
// Increase motor velocity until its length is equal to _maxMotorSpeed.
|
||||
glm::vec3 localVelocity = velocity;
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
|
||||
glm::quat orientation = getHead()->getCameraOrientation();
|
||||
localVelocity = glm::inverse(orientation) * velocity;
|
||||
}
|
||||
|
||||
// Compute keyboard input
|
||||
glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT;
|
||||
glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT;
|
||||
glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP;
|
||||
|
||||
glm::vec3 direction = front + right + up;
|
||||
float directionLength = glm::length(direction);
|
||||
|
||||
// Compute motor magnitude
|
||||
if (directionLength > EPSILON) {
|
||||
direction /= directionLength;
|
||||
// the finalMotorSpeed depends on whether we are walking or not
|
||||
float finalMaxMotorSpeed = walkingOnFloor ? _scale * MAX_WALKING_SPEED : _scale * _maxMotorSpeed;
|
||||
|
||||
float motorLength = glm::length(_motorVelocity);
|
||||
if (motorLength < _scale * MIN_KEYBOARD_CONTROL_SPEED) {
|
||||
// an active keyboard motor should never be slower than this
|
||||
_motorVelocity = _scale * MIN_KEYBOARD_CONTROL_SPEED * direction;
|
||||
} else {
|
||||
float MOTOR_LENGTH_TIMESCALE = 1.5f;
|
||||
float tau = glm::clamp(deltaTime / MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f);
|
||||
float INCREASE_FACTOR = 2.0f;
|
||||
//_motorVelocity *= 1.0f + tau * INCREASE_FACTOR;
|
||||
motorLength *= 1.0f + tau * INCREASE_FACTOR;
|
||||
if (motorLength > finalMaxMotorSpeed) {
|
||||
motorLength = finalMaxMotorSpeed;
|
||||
}
|
||||
_motorVelocity = motorLength * direction;
|
||||
}
|
||||
_isPushing = true;
|
||||
} else {
|
||||
// motor opposes motion (wants to be at rest)
|
||||
_motorVelocity = - localVelocity;
|
||||
}
|
||||
}
|
||||
|
||||
// apply motor
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED) {
|
||||
glm::vec3 targetVelocity = _motorVelocity;
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
|
||||
// rotate _motorVelocity into world frame
|
||||
glm::quat rotation = getHead()->getCameraOrientation();
|
||||
targetVelocity = rotation * _motorVelocity;
|
||||
}
|
||||
|
||||
glm::vec3 targetDirection(0.0f);
|
||||
if (glm::length2(targetVelocity) > EPSILON) {
|
||||
targetDirection = glm::normalize(targetVelocity);
|
||||
}
|
||||
glm::vec3 deltaVelocity = targetVelocity - velocity;
|
||||
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY && glm::length2(_gravity) > EPSILON) {
|
||||
// For now we subtract the component parallel to gravity but what we need to do is:
|
||||
// TODO: subtract the component perp to the local surface normal (motor only pushes in surface plane).
|
||||
glm::vec3 gravityDirection = glm::normalize(_gravity);
|
||||
glm::vec3 parallelDelta = glm::dot(deltaVelocity, gravityDirection) * gravityDirection;
|
||||
if (glm::dot(targetVelocity, velocity) > 0.0f) {
|
||||
// remove parallel part from deltaVelocity
|
||||
deltaVelocity -= parallelDelta;
|
||||
}
|
||||
}
|
||||
|
||||
// simple critical damping
|
||||
float timescale = computeMotorTimescale(velocity);
|
||||
float tau = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
|
||||
velocity += tau * deltaVelocity;
|
||||
}
|
||||
|
||||
// apply thrust
|
||||
velocity += _thrust * deltaTime;
|
||||
speed = glm::length(velocity);
|
||||
if (speed > MAX_AVATAR_SPEED) {
|
||||
velocity *= MAX_AVATAR_SPEED / speed;
|
||||
speed = MAX_AVATAR_SPEED;
|
||||
}
|
||||
_thrust = glm::vec3(0.0f);
|
||||
|
||||
// update position
|
||||
if (glm::length2(_velocity) < EPSILON) {
|
||||
_velocity = glm::vec3(0.0f);
|
||||
} else {
|
||||
_position += _velocity * deltaTime;
|
||||
const float MIN_AVATAR_SPEED = 0.075f;
|
||||
if (speed > MIN_AVATAR_SPEED) {
|
||||
_position += velocity * deltaTime;
|
||||
}
|
||||
updateAcceleration(deltaTime);
|
||||
}
|
||||
|
||||
// update moving flag based on speed
|
||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||
_moving = glm::length(_velocity) > MOVING_SPEED_THRESHOLD;
|
||||
_moving = speed > MOVING_SPEED_THRESHOLD;
|
||||
|
||||
updateChatCircle(deltaTime);
|
||||
measureMotionDerivatives(deltaTime);
|
||||
}
|
||||
|
||||
void MyAvatar::updateMotorFromKeyboard(float deltaTime, bool walking) {
|
||||
// Increase motor velocity until its length is equal to _maxMotorSpeed.
|
||||
if (!(_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED)) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 localVelocity = _velocity;
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
|
||||
glm::quat orientation = getHead()->getCameraOrientation();
|
||||
localVelocity = glm::inverse(orientation) * _velocity;
|
||||
}
|
||||
|
||||
// Compute keyboard input
|
||||
glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT;
|
||||
glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT;
|
||||
glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP;
|
||||
|
||||
glm::vec3 direction = front + right + up;
|
||||
float directionLength = glm::length(direction);
|
||||
|
||||
// Compute motor magnitude
|
||||
if (directionLength > EPSILON) {
|
||||
direction /= directionLength;
|
||||
// the finalMotorSpeed depends on whether we are walking or not
|
||||
float finalMaxMotorSpeed = walking ? _scale * MAX_WALKING_SPEED : _scale * _maxMotorSpeed;
|
||||
|
||||
float motorLength = glm::length(_motorVelocity);
|
||||
if (motorLength < _scale * MIN_KEYBOARD_CONTROL_SPEED) {
|
||||
// an active keyboard motor should never be slower than this
|
||||
_motorVelocity = _scale * MIN_KEYBOARD_CONTROL_SPEED * direction;
|
||||
} else {
|
||||
float MOTOR_LENGTH_TIMESCALE = 1.5f;
|
||||
float tau = glm::clamp(deltaTime / MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f);
|
||||
float INCREASE_FACTOR = 2.0f;
|
||||
//_motorVelocity *= 1.0f + tau * INCREASE_FACTOR;
|
||||
motorLength *= 1.0f + tau * INCREASE_FACTOR;
|
||||
if (motorLength > finalMaxMotorSpeed) {
|
||||
motorLength = finalMaxMotorSpeed;
|
||||
}
|
||||
_motorVelocity = motorLength * direction;
|
||||
}
|
||||
_isPushing = true;
|
||||
} else {
|
||||
// motor opposes motion (wants to be at rest)
|
||||
_motorVelocity = - localVelocity;
|
||||
}
|
||||
}
|
||||
|
||||
float MyAvatar::computeMotorTimescale() {
|
||||
float MyAvatar::computeMotorTimescale(const glm::vec3& velocity) {
|
||||
// The timescale of the motor is the approximate time it takes for the motor to
|
||||
// accomplish its intended velocity. A short timescale makes the motor strong,
|
||||
// and a long timescale makes it weak. The value of timescale to use depends
|
||||
|
@ -1304,7 +1338,7 @@ float MyAvatar::computeMotorTimescale() {
|
|||
timescale = _motorTimescale;
|
||||
_isBraking = false;
|
||||
} else {
|
||||
float speed = glm::length(_velocity);
|
||||
float speed = glm::length(velocity);
|
||||
_isBraking = _wasPushing || (_isBraking && speed > MIN_BRAKE_SPEED);
|
||||
if (_isBraking) {
|
||||
timescale = MIN_MOTOR_TIMESCALE;
|
||||
|
@ -1315,160 +1349,6 @@ float MyAvatar::computeMotorTimescale() {
|
|||
return timescale;
|
||||
}
|
||||
|
||||
void MyAvatar::applyMotor(float deltaTime) {
|
||||
// TODO: recover extra braking behavior when flying close to nearest avatar
|
||||
if (!( _motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED)) {
|
||||
// nothing to do --> early exit
|
||||
return;
|
||||
}
|
||||
glm::vec3 targetVelocity = _motorVelocity;
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
|
||||
// rotate _motorVelocity into world frame
|
||||
glm::quat rotation = getHead()->getCameraOrientation();
|
||||
targetVelocity = rotation * _motorVelocity;
|
||||
}
|
||||
|
||||
glm::vec3 targetDirection(0.0f);
|
||||
if (glm::length2(targetVelocity) > EPSILON) {
|
||||
targetDirection = glm::normalize(targetVelocity);
|
||||
}
|
||||
glm::vec3 deltaVelocity = targetVelocity - _velocity;
|
||||
|
||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY && glm::length2(_gravity) > EPSILON) {
|
||||
// For now we subtract the component parallel to gravity but what we need to do is:
|
||||
// TODO: subtract the component perp to the local surface normal (motor only pushes in surface plane).
|
||||
glm::vec3 gravityDirection = glm::normalize(_gravity);
|
||||
glm::vec3 parallelDelta = glm::dot(deltaVelocity, gravityDirection) * gravityDirection;
|
||||
if (glm::dot(targetVelocity, _velocity) > 0.0f) {
|
||||
// remove parallel part from deltaVelocity
|
||||
deltaVelocity -= parallelDelta;
|
||||
}
|
||||
}
|
||||
|
||||
// simple critical damping
|
||||
float timescale = computeMotorTimescale();
|
||||
float tau = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
|
||||
_velocity += tau * deltaVelocity;
|
||||
}
|
||||
|
||||
void MyAvatar::applyThrust(float deltaTime) {
|
||||
_velocity += _thrust * deltaTime;
|
||||
float speed = glm::length(_velocity);
|
||||
// cap the speed that thrust can achieve
|
||||
if (speed > MAX_AVATAR_SPEED) {
|
||||
_velocity *= MAX_AVATAR_SPEED / speed;
|
||||
}
|
||||
// zero thrust so we don't pile up thrust from other sources
|
||||
_thrust = glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
/* Keep this code for the short term as reference in case we need to further tune the new model
|
||||
* to achieve legacy movement response.
|
||||
void MyAvatar::updateThrust(float deltaTime) {
|
||||
//
|
||||
// Gather thrust information from keyboard and sensors to apply to avatar motion
|
||||
//
|
||||
glm::quat orientation = getHead()->getCameraOrientation();
|
||||
glm::vec3 front = orientation * IDENTITY_FRONT;
|
||||
glm::vec3 right = orientation * IDENTITY_RIGHT;
|
||||
glm::vec3 up = orientation * IDENTITY_UP;
|
||||
|
||||
const float THRUST_MAG_UP = 800.0f;
|
||||
const float THRUST_MAG_DOWN = 300.0f;
|
||||
const float THRUST_MAG_FWD = 500.0f;
|
||||
const float THRUST_MAG_BACK = 300.0f;
|
||||
const float THRUST_MAG_LATERAL = 250.0f;
|
||||
const float THRUST_JUMP = 120.0f;
|
||||
|
||||
// Add Thrusts from keyboard
|
||||
_thrust += _driveKeys[FWD] * _scale * THRUST_MAG_FWD * _thrustMultiplier * deltaTime * front;
|
||||
_thrust -= _driveKeys[BACK] * _scale * THRUST_MAG_BACK * _thrustMultiplier * deltaTime * front;
|
||||
_thrust += _driveKeys[RIGHT] * _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right;
|
||||
_thrust -= _driveKeys[LEFT] * _scale * THRUST_MAG_LATERAL * _thrustMultiplier * deltaTime * right;
|
||||
_thrust += _driveKeys[UP] * _scale * THRUST_MAG_UP * _thrustMultiplier * deltaTime * up;
|
||||
_thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up;
|
||||
|
||||
// attenuate thrust when in penetration
|
||||
if (glm::dot(_thrust, _lastBodyPenetration) > EPSILON) {
|
||||
const float MAX_BODY_PENETRATION_DEPTH = 0.6f * _skeletonModel.getBoundingShapeRadius();
|
||||
float penetrationFactor = glm::min(1.0f, glm::length(_lastBodyPenetration) / MAX_BODY_PENETRATION_DEPTH);
|
||||
glm::vec3 penetrationDirection = glm::normalize(_lastBodyPenetration);
|
||||
// attenuate parallel component
|
||||
glm::vec3 parallelThrust = glm::dot(_thrust, penetrationDirection) * penetrationDirection;
|
||||
// attenuate perpendicular component (friction)
|
||||
glm::vec3 perpendicularThrust = _thrust - parallelThrust;
|
||||
// recombine to get the final thrust
|
||||
_thrust = (1.0f - penetrationFactor) * parallelThrust + (1.0f - penetrationFactor * penetrationFactor) * perpendicularThrust;
|
||||
|
||||
// attenuate the growth of _thrustMultiplier when in penetration
|
||||
// otherwise the avatar will eventually be able to tunnel through the obstacle
|
||||
_thrustMultiplier *= (1.0f - penetrationFactor * penetrationFactor);
|
||||
} else if (_thrustMultiplier < 1.0f) {
|
||||
// rapid healing of attenuated thrustMultiplier after penetration event
|
||||
_thrustMultiplier = 1.0f;
|
||||
}
|
||||
_lastBodyPenetration = glm::vec3(0.0f);
|
||||
|
||||
// If thrust keys are being held down, slowly increase thrust to allow reaching great speeds
|
||||
if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) {
|
||||
const float THRUST_INCREASE_RATE = 1.05f;
|
||||
const float MAX_THRUST_MULTIPLIER = 75.0f;
|
||||
_thrustMultiplier *= 1.0f + deltaTime * THRUST_INCREASE_RATE;
|
||||
if (_thrustMultiplier > MAX_THRUST_MULTIPLIER) {
|
||||
_thrustMultiplier = MAX_THRUST_MULTIPLIER;
|
||||
}
|
||||
} else {
|
||||
_thrustMultiplier = 1.0f;
|
||||
}
|
||||
|
||||
// Add one time jumping force if requested
|
||||
if (_shouldJump) {
|
||||
if (glm::length(_gravity) > EPSILON) {
|
||||
_thrust += _scale * THRUST_JUMP * up;
|
||||
}
|
||||
_shouldJump = false;
|
||||
}
|
||||
|
||||
// Update speed brake status
|
||||
const float MIN_SPEED_BRAKE_VELOCITY = _scale * 0.4f;
|
||||
if ((glm::length(_thrust) == 0.0f) && _isThrustOn && (glm::length(_velocity) > MIN_SPEED_BRAKE_VELOCITY)) {
|
||||
_speedBrakes = true;
|
||||
}
|
||||
_isThrustOn = (glm::length(_thrust) > EPSILON);
|
||||
|
||||
if (_isThrustOn || (_speedBrakes && (glm::length(_velocity) < MIN_SPEED_BRAKE_VELOCITY))) {
|
||||
_speedBrakes = false;
|
||||
}
|
||||
_velocity += _thrust * deltaTime;
|
||||
|
||||
// Zero thrust out now that we've added it to velocity in this frame
|
||||
_thrust = glm::vec3(0.0f);
|
||||
|
||||
// apply linear damping
|
||||
const float MAX_STATIC_FRICTION_SPEED = 0.5f;
|
||||
const float STATIC_FRICTION_STRENGTH = _scale * 20.0f;
|
||||
applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_SPEED, STATIC_FRICTION_STRENGTH);
|
||||
|
||||
const float LINEAR_DAMPING_STRENGTH = 0.5f;
|
||||
const float SPEED_BRAKE_POWER = _scale * 10.0f;
|
||||
const float SQUARED_DAMPING_STRENGTH = 0.007f;
|
||||
|
||||
const float SLOW_NEAR_RADIUS = 5.0f;
|
||||
float linearDamping = LINEAR_DAMPING_STRENGTH;
|
||||
const float NEAR_AVATAR_DAMPING_FACTOR = 50.0f;
|
||||
if (_distanceToNearestAvatar < _scale * SLOW_NEAR_RADIUS) {
|
||||
linearDamping *= 1.0f + NEAR_AVATAR_DAMPING_FACTOR *
|
||||
((SLOW_NEAR_RADIUS - _distanceToNearestAvatar) / SLOW_NEAR_RADIUS);
|
||||
}
|
||||
if (_speedBrakes) {
|
||||
applyDamping(deltaTime, _velocity, linearDamping * SPEED_BRAKE_POWER, SQUARED_DAMPING_STRENGTH * SPEED_BRAKE_POWER);
|
||||
} else {
|
||||
applyDamping(deltaTime, _velocity, linearDamping, SQUARED_DAMPING_STRENGTH);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
|
||||
glm::vec3 up = getBodyUpDirection();
|
||||
const float ENVIRONMENT_SURFACE_ELASTICITY = 0.0f;
|
||||
|
|
|
@ -54,7 +54,6 @@ public:
|
|||
|
||||
// setters
|
||||
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
|
||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
||||
void setLeanScale(float scale) { _leanScale = scale; }
|
||||
void setLocalGravity(glm::vec3 gravity);
|
||||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
|
||||
|
@ -189,23 +188,23 @@ private:
|
|||
bool _mousePressed;
|
||||
float _bodyPitchDelta; // degrees
|
||||
float _bodyRollDelta; // degrees
|
||||
bool _shouldJump;
|
||||
float _driveKeys[MAX_DRIVE_KEYS];
|
||||
glm::vec3 _gravity;
|
||||
float _distanceToNearestAvatar; // How close is the nearest avatar?
|
||||
|
||||
bool _shouldJump;
|
||||
float _driveKeys[MAX_DRIVE_KEYS];
|
||||
bool _wasPushing;
|
||||
bool _isPushing;
|
||||
bool _isBraking;
|
||||
float _trapDuration; // seconds that avatar has been trapped by collisions
|
||||
glm::vec3 _thrust; // final acceleration from outside sources for the current frame
|
||||
|
||||
glm::vec3 _motorVelocity; // intended velocity of avatar motion
|
||||
float _trapDuration; // seconds that avatar has been trapped by collisions
|
||||
glm::vec3 _thrust; // impulse accumulator for outside sources
|
||||
|
||||
glm::vec3 _motorVelocity; // intended velocity of avatar motion (relative to what it's standing on)
|
||||
float _motorTimescale; // timescale for avatar motor to achieve its desired velocity
|
||||
float _maxMotorSpeed;
|
||||
quint32 _motionBehaviors;
|
||||
|
||||
glm::vec3 _lastBodyPenetration;
|
||||
glm::vec3 _lastFloorContactPoint;
|
||||
QWeakPointer<AvatarData> _lookAtTargetAvatar;
|
||||
glm::vec3 _targetAvatarPosition;
|
||||
|
@ -223,10 +222,7 @@ private:
|
|||
float computeDistanceToFloor(const glm::vec3& startPoint);
|
||||
void updateOrientation(float deltaTime);
|
||||
void updatePosition(float deltaTime);
|
||||
void updateMotorFromKeyboard(float deltaTime, bool walking);
|
||||
float computeMotorTimescale();
|
||||
void applyMotor(float deltaTime);
|
||||
void applyThrust(float deltaTime);
|
||||
float computeMotorTimescale(const glm::vec3& velocity);
|
||||
void updateCollisionWithAvatars(float deltaTime);
|
||||
void updateCollisionWithEnvironment(float deltaTime, float radius);
|
||||
void updateCollisionWithVoxels(float deltaTime, float radius);
|
||||
|
|
|
@ -140,7 +140,7 @@ QOpenGLFramebufferObject* GlowEffect::render(bool toTexture) {
|
|||
|
||||
QOpenGLFramebufferObject* destFBO = toTexture ?
|
||||
Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject() : NULL;
|
||||
if (_isEmpty && _renderMode != DIFFUSE_ADD_MODE) {
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect) || (_isEmpty && _renderMode != DIFFUSE_ADD_MODE)) {
|
||||
// copy the primary to the screen
|
||||
if (QOpenGLFramebufferObject::hasOpenGLFramebufferBlit()) {
|
||||
QOpenGLFramebufferObject::blitFramebuffer(destFBO, primaryFBO);
|
||||
|
|
|
@ -1251,8 +1251,11 @@ QColor VoxelColorBoxTool::getColor() {
|
|||
}
|
||||
|
||||
void VoxelColorBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
|
||||
// ensure that color is either 100% transparent or 100% opaque
|
||||
QColor color = _color->getColor();
|
||||
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelColorBoxEdit(Box(minimum, maximum),
|
||||
_editor->getGridSpacing(), _color->getColor())) };
|
||||
_editor->getGridSpacing(), color)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
|
@ -1279,8 +1282,13 @@ QColor VoxelMaterialBoxTool::getColor() {
|
|||
void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
|
||||
SharedObjectPointer material = _materialEditor->getObject();
|
||||
_materialEditor->detachObject();
|
||||
QColor color;
|
||||
if (_texture) {
|
||||
color = _texture->getAverageColor();
|
||||
color.setAlphaF(1.0f);
|
||||
}
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialBoxEdit(Box(minimum, maximum),
|
||||
_editor->getGridSpacing(), material, _texture ? _texture->getAverageColor() : QColor())) };
|
||||
_editor->getGridSpacing(), material, color)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
|
@ -1363,8 +1371,11 @@ QColor VoxelColorSphereTool::getColor() {
|
|||
}
|
||||
|
||||
void VoxelColorSphereTool::applyValue(const glm::vec3& position, float radius) {
|
||||
// ensure that color is either 100% transparent or 100% opaque
|
||||
QColor color = _color->getColor();
|
||||
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelColorSphereEdit(position, radius,
|
||||
_editor->getGridSpacing(), _color->getColor())) };
|
||||
_editor->getGridSpacing(), color)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
|
@ -1386,8 +1397,13 @@ QColor VoxelMaterialSphereTool::getColor() {
|
|||
void VoxelMaterialSphereTool::applyValue(const glm::vec3& position, float radius) {
|
||||
SharedObjectPointer material = _materialEditor->getObject();
|
||||
_materialEditor->detachObject();
|
||||
QColor color;
|
||||
if (_texture) {
|
||||
color = _texture->getAverageColor();
|
||||
color.setAlphaF(1.0f);
|
||||
}
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSphereEdit(position, radius,
|
||||
_editor->getGridSpacing(), material, _texture ? _texture->getAverageColor() : QColor())) };
|
||||
_editor->getGridSpacing(), material, color)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,9 @@ void AudioInjector::injectAudio() {
|
|||
int numPreSequenceNumberBytes = injectAudioPacket.size();
|
||||
packetStream << (quint16)0;
|
||||
|
||||
// pack the stereo/mono type of the stream
|
||||
packetStream << _options.isStereo();
|
||||
|
||||
// pack stream identifier (a generated UUID)
|
||||
packetStream << QUuid::createUuid();
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ AudioInjectorOptions::AudioInjectorOptions(QObject* parent) :
|
|||
_volume(1.0f),
|
||||
_loop(false),
|
||||
_orientation(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
_isStereo(false),
|
||||
_loopbackAudioInterface(NULL)
|
||||
{
|
||||
}
|
||||
|
@ -26,6 +27,7 @@ AudioInjectorOptions::AudioInjectorOptions(const AudioInjectorOptions& other) {
|
|||
_volume = other._volume;
|
||||
_loop = other._loop;
|
||||
_orientation = other._orientation;
|
||||
_isStereo = other._isStereo;
|
||||
_loopbackAudioInterface = other._loopbackAudioInterface;
|
||||
}
|
||||
|
||||
|
@ -34,5 +36,6 @@ void AudioInjectorOptions::operator=(const AudioInjectorOptions& other) {
|
|||
_volume = other._volume;
|
||||
_loop = other._loop;
|
||||
_orientation = other._orientation;
|
||||
_isStereo = other._isStereo;
|
||||
_loopbackAudioInterface = other._loopbackAudioInterface;
|
||||
}
|
|
@ -44,6 +44,9 @@ public:
|
|||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
void setOrientation(const glm::quat& orientation) { _orientation = orientation; }
|
||||
|
||||
const bool isStereo() const { return _isStereo; }
|
||||
void setIsStereo(const bool isStereo) { _isStereo = isStereo; }
|
||||
|
||||
AbstractAudioInterface* getLoopbackAudioInterface() const { return _loopbackAudioInterface; }
|
||||
void setLoopbackAudioInterface(AbstractAudioInterface* loopbackAudioInterface)
|
||||
{ _loopbackAudioInterface = loopbackAudioInterface; }
|
||||
|
@ -52,6 +55,7 @@ private:
|
|||
float _volume;
|
||||
bool _loop;
|
||||
glm::quat _orientation;
|
||||
bool _isStereo;
|
||||
AbstractAudioInterface* _loopbackAudioInterface;
|
||||
};
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray
|
|||
|
||||
// skip the stream identifier
|
||||
packetStream.skipRawData(NUM_BYTES_RFC4122_UUID);
|
||||
|
||||
packetStream >> _isStereo;
|
||||
|
||||
// pull the loopback flag and set our boolean
|
||||
uchar shouldLoopback;
|
||||
|
|
|
@ -240,8 +240,6 @@ public:
|
|||
const HeadData* getHeadData() const { return _headData; }
|
||||
const HandData* getHandData() const { return _handData; }
|
||||
|
||||
virtual const glm::vec3& getVelocity() const { return vec3Zero; }
|
||||
|
||||
virtual bool findSphereCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -467,7 +467,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
if (existingEntity) {
|
||||
updateEntity(entityItemID, properties);
|
||||
} else {
|
||||
qDebug() << "User attempted to edit an unknown entity.";
|
||||
qDebug() << "User attempted to edit an unknown entity. ID:" << entityItemID;
|
||||
}
|
||||
} else {
|
||||
// this is a new entity... assign a new entityID
|
||||
|
|
|
@ -67,7 +67,8 @@ public:
|
|||
virtual int minimumRequiredRootDataBytes() const { return sizeof(uint16_t); }
|
||||
virtual bool suppressEmptySubtrees() const { return false; }
|
||||
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const;
|
||||
|
||||
virtual bool mustIncludeAllChildData() const { return false; }
|
||||
|
||||
virtual bool versionHasSVOfileBreaks(PacketVersion thisVersion) const
|
||||
{ return thisVersion >= VERSION_ENTITIES_HAS_FILE_BREAKS; }
|
||||
|
||||
|
@ -126,7 +127,7 @@ public:
|
|||
void setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element);
|
||||
void resetContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element);
|
||||
void debugDumpMap();
|
||||
void dumpTree();
|
||||
virtual void dumpTree();
|
||||
|
||||
void sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z);
|
||||
|
||||
|
|
|
@ -721,9 +721,17 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
|||
if (bytesLeftToRead >= (int)(numberOfEntities * expectedBytesPerEntity)) {
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
int bytesForThisEntity = 0;
|
||||
EntityItemID entityItemID = EntityItemID::readEntityItemIDFromBuffer(dataAt, bytesLeftToRead);
|
||||
EntityItem* entityItem = _myTree->findEntityByEntityItemID(entityItemID);
|
||||
EntityItemID entityItemID;
|
||||
EntityItem* entityItem = NULL;
|
||||
bool newEntity = false;
|
||||
|
||||
// Old model files don't have UUIDs in them. So we don't want to try to read those IDs from the stream.
|
||||
// Since this can only happen on loading an old file, we can safely treat these as new entity cases,
|
||||
// which will correctly handle the case of creating models and letting them parse the old format.
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_SUPPORT_SPLIT_MTU) {
|
||||
entityItemID = EntityItemID::readEntityItemIDFromBuffer(dataAt, bytesLeftToRead);
|
||||
entityItem = _myTree->findEntityByEntityItemID(entityItemID);
|
||||
}
|
||||
|
||||
// If the item already exists in our tree, we want do the following...
|
||||
// 1) allow the existing item to read from the databuffer
|
||||
|
@ -758,6 +766,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
|||
if (entityItem) {
|
||||
bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
|
||||
addEntityItem(entityItem); // add this new entity to this elements entities
|
||||
entityItemID = entityItem->getEntityItemID();
|
||||
_myTree->setContainingElement(entityItemID, this);
|
||||
newEntity = true;
|
||||
EntityItem::SimulationState newState = entityItem->getSimulationState();
|
||||
|
|
|
@ -65,7 +65,9 @@ bool EntityTypes::registerEntityType(EntityType entityType, const char* name, En
|
|||
return false;
|
||||
}
|
||||
|
||||
EntityItem* EntityTypes::constructEntityItem(EntityType entityType, const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItem* EntityTypes::constructEntityItem(EntityType entityType, const EntityItemID& entityID,
|
||||
const EntityItemProperties& properties) {
|
||||
|
||||
EntityItem* newEntityItem = NULL;
|
||||
EntityTypeFactory factory = NULL;
|
||||
if (entityType >= 0 && entityType <= LAST) {
|
||||
|
|
|
@ -165,7 +165,8 @@ int ModelEntityItem::oldVersionReadEntityDataFromBuffer(const unsigned char* dat
|
|||
|
||||
QString ageAsString = formatSecondsElapsed(getAge());
|
||||
qDebug() << "Loading old model file, _created = _lastEdited =" << _created
|
||||
<< " age=" << getAge() << "seconds - " << ageAsString;
|
||||
<< " age=" << getAge() << "seconds - " << ageAsString
|
||||
<< "old ID=" << oldID << "new ID=" << _id;
|
||||
|
||||
// radius
|
||||
memcpy(&_radius, dataAt, sizeof(_radius));
|
||||
|
|
|
@ -2053,19 +2053,64 @@ FBXGeometry readSVO(const QByteArray& model) {
|
|||
mesh.parts.append(part);
|
||||
|
||||
VoxelTree tree;
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS);
|
||||
|
||||
unsigned char* dataAt = (unsigned char*)model.data();
|
||||
size_t dataSize = model.size();
|
||||
|
||||
if (tree.getWantSVOfileVersions()) {
|
||||
PacketVersion gotVersion = 0;
|
||||
|
||||
// NOTE: SPECIAL CASE for old voxel svo files. The old voxel SVO files didn't have header
|
||||
// details. They started with the the octalcode for the root. Which was always 00 which matches PacketTypeUnknown
|
||||
unsigned char* firstByteAt = (unsigned char*)model.data();
|
||||
unsigned char firstByteValue = *firstByteAt;
|
||||
if (tree.expectedDataPacketType() == PacketTypeVoxelData && firstByteValue == 0) {
|
||||
qDebug() << "Detected OLD Voxels format.";
|
||||
gotVersion = 0;
|
||||
} else if (tree.getWantSVOfileVersions()) {
|
||||
// skip the type/version
|
||||
dataAt += sizeof(PacketType);
|
||||
dataSize -= sizeof(PacketType);
|
||||
|
||||
gotVersion = *dataAt;
|
||||
dataAt += sizeof(PacketVersion);
|
||||
dataSize -= sizeof(PacketVersion);
|
||||
}
|
||||
tree.readBitstreamToTree(dataAt, dataSize, args);
|
||||
}
|
||||
bool hasBufferBreaks = tree.versionHasSVOfileBreaks(gotVersion);
|
||||
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
|
||||
SharedNodePointer(), false, gotVersion);
|
||||
|
||||
if (!hasBufferBreaks) {
|
||||
tree.readBitstreamToTree(dataAt, dataSize, args);
|
||||
} else {
|
||||
const unsigned long MAX_CHUNK_LENGTH = MAX_OCTREE_PACKET_SIZE * 2;
|
||||
while (dataSize > 0) {
|
||||
quint16 chunkLength = 0;
|
||||
|
||||
chunkLength = *dataAt;
|
||||
dataAt += sizeof(chunkLength);
|
||||
dataSize -= sizeof(chunkLength);
|
||||
|
||||
if (chunkLength > dataSize) {
|
||||
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
|
||||
<< "greater than remaining length:" << dataSize;
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunkLength > MAX_CHUNK_LENGTH) {
|
||||
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
|
||||
<< "greater than MAX_CHUNK_LENGTH:" << MAX_CHUNK_LENGTH;
|
||||
break;
|
||||
}
|
||||
|
||||
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
|
||||
SharedNodePointer(), false, gotVersion);
|
||||
|
||||
tree.readBitstreamToTree(dataAt, chunkLength, args);
|
||||
dataAt += chunkLength;
|
||||
dataSize -= chunkLength;
|
||||
}
|
||||
}
|
||||
tree.recurseTreeWithOperation(addMeshVoxelsOperation, &mesh);
|
||||
|
||||
geometry.meshes.append(mesh);
|
||||
|
|
|
@ -83,11 +83,75 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
return 1;
|
||||
case PacketTypeMetavoxelData:
|
||||
return 3;
|
||||
case PacketTypeVoxelData:
|
||||
return VERSION_VOXELS_HAS_FILE_BREAKS;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define PACKET_TYPE_NAME_LOOKUP(x) case x: return QString(#x);
|
||||
|
||||
QString nameForPacketType(PacketType type) {
|
||||
switch (type) {
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeUnknown);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeStunResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainList);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypePing);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypePingReply);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeKillAvatar);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeInjectAudio);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMixedAudio);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMicrophoneAudioNoEcho);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMicrophoneAudioWithEcho);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeBulkAvatarData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeSilentAudioFrame);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEnvironmentData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainListRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeRequestAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeCreateAssignment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainOAuthRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMuteEnvironment);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAudioStreamStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDataServerConfirm);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeVoxelQuery);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeVoxelData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeVoxelSet);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeVoxelSetDestructive);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeVoxelErase);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeOctreeStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeJurisdiction);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeJurisdictionRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeParticleQuery);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeParticleData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeParticleAddOrEdit);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeParticleErase);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeParticleAddResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeMetavoxelData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarIdentity);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeAvatarBillboard);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainConnectRequest);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainServerRequireDTLS);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeNodeJsonStats);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityQuery);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityData);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAddOrEdit);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityErase);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAddResponse);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeOctreeDataNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeVoxelEditNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeParticleEditNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityEditNack);
|
||||
PACKET_TYPE_NAME_LOOKUP(PacketTypeSignedTransactionPayment);
|
||||
default:
|
||||
return QString("Type: ") + QString::number((int)type);
|
||||
}
|
||||
return QString("unexpected");
|
||||
}
|
||||
|
||||
|
||||
|
||||
QByteArray byteArrayWithPopulatedHeader(PacketType type, const QUuid& connectionUUID) {
|
||||
QByteArray freshByteArray(MAX_PACKET_HEADER_BYTES, 0);
|
||||
freshByteArray.resize(populatePacketHeader(freshByteArray, type, connectionUUID));
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "UUID.h"
|
||||
|
||||
// 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
|
||||
enum PacketType {
|
||||
PacketTypeUnknown,
|
||||
PacketTypeStunResponse,
|
||||
|
@ -87,6 +87,7 @@ const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UU
|
|||
const int MAX_PACKET_HEADER_BYTES = sizeof(PacketType) + NUM_BYTES_MD5_HASH + NUM_STATIC_HEADER_BYTES;
|
||||
|
||||
PacketVersion versionForPacketType(PacketType type);
|
||||
QString nameForPacketType(PacketType type);
|
||||
|
||||
const QUuid nullUUID = QUuid();
|
||||
|
||||
|
@ -116,5 +117,6 @@ const PacketVersion VERSION_ENTITIES_HAVE_ANIMATION = 1;
|
|||
const PacketVersion VERSION_ROOT_ELEMENT_HAS_DATA = 2;
|
||||
const PacketVersion VERSION_ENTITIES_SUPPORT_SPLIT_MTU = 3;
|
||||
const PacketVersion VERSION_ENTITIES_HAS_FILE_BREAKS = VERSION_ENTITIES_SUPPORT_SPLIT_MTU;
|
||||
const PacketVersion VERSION_VOXELS_HAS_FILE_BREAKS = 1;
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
|
@ -233,15 +233,31 @@ OctreeElement* Octree::createMissingElement(OctreeElement* lastParentElement, co
|
|||
}
|
||||
}
|
||||
|
||||
int Octree::readElementData(OctreeElement* destinationElement, const unsigned char* nodeData, int bytesLeftToRead,
|
||||
int Octree::readElementData(OctreeElement* destinationElement, const unsigned char* nodeData, int bytesAvailable,
|
||||
ReadBitstreamToTreeParams& args) {
|
||||
|
||||
int bytesLeftToRead = bytesAvailable;
|
||||
int bytesRead = 0;
|
||||
|
||||
// give this destination element the child mask from the packet
|
||||
const unsigned char ALL_CHILDREN_ASSUMED_TO_EXIST = 0xFF;
|
||||
|
||||
if (bytesLeftToRead < sizeof(unsigned char)) {
|
||||
qDebug() << "UNEXPECTED: readElementData() only had " << bytesLeftToRead << " bytes. Not enough for meaningful data.";
|
||||
return bytesAvailable; // assume we read the entire buffer...
|
||||
}
|
||||
|
||||
if (destinationElement->getScale() < SCALE_AT_DANGEROUSLY_DEEP_RECURSION) {
|
||||
qDebug() << "UNEXPECTED: readElementData() destination element is unreasonably small ["
|
||||
<< destinationElement->getScale() * (float)TREE_SCALE << " meters] "
|
||||
<< " Discarding " << bytesAvailable << " remaining bytes.";
|
||||
return bytesAvailable; // assume we read the entire buffer...
|
||||
}
|
||||
|
||||
unsigned char colorInPacketMask = *nodeData;
|
||||
bytesRead += sizeof(colorInPacketMask);
|
||||
bytesLeftToRead -= sizeof(colorInPacketMask);
|
||||
|
||||
// instantiate variable for bytes already read
|
||||
int bytesRead = sizeof(colorInPacketMask);
|
||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
// check the colors mask to see if we have a child to color in
|
||||
if (oneAtBit(colorInPacketMask, i)) {
|
||||
|
@ -256,9 +272,13 @@ int Octree::readElementData(OctreeElement* destinationElement, const unsigned ch
|
|||
OctreeElement* childElementAt = destinationElement->getChildAtIndex(i);
|
||||
bool nodeIsDirty = false;
|
||||
if (childElementAt) {
|
||||
bytesRead += childElementAt->readElementDataFromBuffer(nodeData + bytesRead, bytesLeftToRead, args);
|
||||
|
||||
int childElementDataRead = childElementAt->readElementDataFromBuffer(nodeData + bytesRead, bytesLeftToRead, args);
|
||||
childElementAt->setSourceUUID(args.sourceUUID);
|
||||
|
||||
bytesRead += childElementDataRead;
|
||||
bytesLeftToRead -= childElementDataRead;
|
||||
|
||||
// if we had a local version of the element already, it's possible that we have it already but
|
||||
// with the same color data, so this won't count as a change. To address this we check the following
|
||||
if (!childElementAt->isDirty() && childElementAt->getShouldRender() && !childElementAt->isRendered()) {
|
||||
|
@ -273,17 +293,28 @@ int Octree::readElementData(OctreeElement* destinationElement, const unsigned ch
|
|||
}
|
||||
}
|
||||
|
||||
// give this destination element the child mask from the packet
|
||||
unsigned char childrenInTreeMask = args.includeExistsBits ? *(nodeData + bytesRead) : ALL_CHILDREN_ASSUMED_TO_EXIST;
|
||||
unsigned char childMask = *(nodeData + bytesRead + (args.includeExistsBits ? sizeof(childrenInTreeMask) : 0));
|
||||
unsigned char childrenInTreeMask = ALL_CHILDREN_ASSUMED_TO_EXIST;
|
||||
unsigned char childInBufferMask = 0;
|
||||
int bytesForMasks = args.includeExistsBits ? sizeof(childrenInTreeMask) + sizeof(childInBufferMask)
|
||||
: sizeof(childInBufferMask);
|
||||
|
||||
if (bytesLeftToRead < bytesForMasks) {
|
||||
qDebug() << "UNEXPECTED: readElementDataFromBuffer() only had " << bytesLeftToRead << " bytes before masks. "
|
||||
"Not enough for meaningful data.";
|
||||
return bytesAvailable; // assume we read the entire buffer...
|
||||
}
|
||||
|
||||
childrenInTreeMask = args.includeExistsBits ? *(nodeData + bytesRead) : ALL_CHILDREN_ASSUMED_TO_EXIST;
|
||||
childInBufferMask = *(nodeData + bytesRead + (args.includeExistsBits ? sizeof(childrenInTreeMask) : 0));
|
||||
|
||||
int childIndex = 0;
|
||||
bytesRead += args.includeExistsBits ? sizeof(childrenInTreeMask) + sizeof(childMask) : sizeof(childMask);
|
||||
bytesRead += bytesForMasks;
|
||||
bytesLeftToRead -= bytesForMasks;
|
||||
|
||||
while (bytesLeftToRead - bytesRead > 0 && childIndex < NUMBER_OF_CHILDREN) {
|
||||
while (bytesLeftToRead > 0 && childIndex < NUMBER_OF_CHILDREN) {
|
||||
// check the exists mask to see if we have a child to traverse into
|
||||
|
||||
if (oneAtBit(childMask, childIndex)) {
|
||||
if (oneAtBit(childInBufferMask, childIndex)) {
|
||||
if (!destinationElement->getChildAtIndex(childIndex)) {
|
||||
// add a child at that index, if it doesn't exist
|
||||
destinationElement->addChildAtIndex(childIndex);
|
||||
|
@ -294,8 +325,11 @@ int Octree::readElementData(OctreeElement* destinationElement, const unsigned ch
|
|||
}
|
||||
|
||||
// tell the child to read the subsequent data
|
||||
bytesRead += readElementData(destinationElement->getChildAtIndex(childIndex),
|
||||
nodeData + bytesRead, bytesLeftToRead - bytesRead, args);
|
||||
int lowerLevelBytes = readElementData(destinationElement->getChildAtIndex(childIndex),
|
||||
nodeData + bytesRead, bytesLeftToRead, args);
|
||||
|
||||
bytesRead += lowerLevelBytes;
|
||||
bytesLeftToRead -= lowerLevelBytes;
|
||||
}
|
||||
childIndex++;
|
||||
}
|
||||
|
@ -314,7 +348,9 @@ int Octree::readElementData(OctreeElement* destinationElement, const unsigned ch
|
|||
// if this is the root, and there is more data to read, allow it to read it's element data...
|
||||
if (destinationElement == _rootElement && rootElementHasData() && (bytesLeftToRead - bytesRead) > 0) {
|
||||
// tell the element to read the subsequent data
|
||||
bytesRead += _rootElement->readElementDataFromBuffer(nodeData + bytesRead, bytesLeftToRead - bytesRead, args);
|
||||
int rootDataSize = _rootElement->readElementDataFromBuffer(nodeData + bytesRead, bytesLeftToRead - bytesRead, args);
|
||||
bytesRead += rootDataSize;
|
||||
bytesLeftToRead -= rootDataSize;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
|
@ -322,7 +358,6 @@ int Octree::readElementData(OctreeElement* destinationElement, const unsigned ch
|
|||
|
||||
void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long int bufferSizeBytes,
|
||||
ReadBitstreamToTreeParams& args) {
|
||||
|
||||
int bytesRead = 0;
|
||||
const unsigned char* bitstreamAt = bitstream;
|
||||
|
||||
|
@ -337,9 +372,18 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long
|
|||
|
||||
while (bitstreamAt < bitstream + bufferSizeBytes) {
|
||||
OctreeElement* bitstreamRootElement = nodeForOctalCode(args.destinationElement, (unsigned char *)bitstreamAt, NULL);
|
||||
if (*bitstreamAt != *bitstreamRootElement->getOctalCode()) {
|
||||
// if the octal code returned is not on the same level as
|
||||
// the code being searched for, we have OctreeElements to create
|
||||
|
||||
int numberOfThreeBitSectionsInStream = numberOfThreeBitSectionsInCode(bitstreamAt, bufferSizeBytes);
|
||||
|
||||
if (numberOfThreeBitSectionsInStream == OVERFLOWED_OCTCODE_BUFFER) {
|
||||
qDebug() << "UNEXPECTED: parsing of the octal code would overflow the buffer. This buffer is corrupt. Returning.";
|
||||
return;
|
||||
}
|
||||
|
||||
int numberOfThreeBitSectionsFromNode = numberOfThreeBitSectionsInCode(bitstreamRootElement->getOctalCode());
|
||||
|
||||
// if the octal code returned is not on the same level as the code being searched for, we have OctreeElements to create
|
||||
if (numberOfThreeBitSectionsInStream != numberOfThreeBitSectionsFromNode) {
|
||||
|
||||
// Note: we need to create this element relative to root, because we're assuming that the bitstream for the initial
|
||||
// octal code is always relative to root!
|
||||
|
@ -349,16 +393,18 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long
|
|||
}
|
||||
}
|
||||
|
||||
int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt);
|
||||
int octalCodeBytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInStream);
|
||||
|
||||
int theseBytesRead = 0;
|
||||
theseBytesRead += octalCodeBytes;
|
||||
theseBytesRead += readElementData(bitstreamRootElement, bitstreamAt + octalCodeBytes,
|
||||
int lowerLevelBytes = readElementData(bitstreamRootElement, bitstreamAt + octalCodeBytes,
|
||||
bufferSizeBytes - (bytesRead + octalCodeBytes), args);
|
||||
|
||||
theseBytesRead += lowerLevelBytes;
|
||||
|
||||
// skip bitstream to new startPoint
|
||||
bitstreamAt += theseBytesRead;
|
||||
bytesRead += theseBytesRead;
|
||||
bytesRead += theseBytesRead;
|
||||
|
||||
if (args.wantImportProgress) {
|
||||
emit importProgress((100 * (bitstreamAt - bitstream)) / bufferSizeBytes);
|
||||
|
@ -1004,7 +1050,7 @@ int Octree::encodeTreeBitstream(OctreeElement* element,
|
|||
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
|
||||
bytesWritten += codeLength; // keep track of byte count
|
||||
|
||||
int currentEncodeLevel = 0;
|
||||
|
@ -1421,6 +1467,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
|||
// We wil write this bit mask but we may come back later and update the bits that are actually included
|
||||
packetData->releaseReservedBytes(sizeof(childrenDataBits));
|
||||
continueThisLevel = packetData->appendBitMask(childrenDataBits);
|
||||
|
||||
int childDataBitsPlaceHolder = packetData->getUncompressedByteOffset(sizeof(childrenDataBits));
|
||||
unsigned char actualChildrenDataBits = 0;
|
||||
|
||||
|
@ -1470,6 +1517,13 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
|||
// Continue this level so long as some part of this child element was appended.
|
||||
bool childFit = (childAppendState != OctreeElement::NONE);
|
||||
|
||||
// some datatypes (like Voxels) assume that all child data will fit, if it doesn't fit
|
||||
// the data type wants to bail on this element level completely
|
||||
if (!childFit && mustIncludeAllChildData()) {
|
||||
continueThisLevel = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the child was partially or fully appended, then mark the actualChildrenDataBits as including
|
||||
// this child data
|
||||
if (childFit) {
|
||||
|
@ -1500,15 +1554,14 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
|||
}
|
||||
}
|
||||
|
||||
if (!continueThisLevel) {
|
||||
if (!mustIncludeAllChildData() && !continueThisLevel) {
|
||||
qDebug() << "WARNING UNEXPECTED CASE: reached end of child element data loop with continueThisLevel=FALSE";
|
||||
qDebug() << "This is not expected!!!! -- continueThisLevel=FALSE....";
|
||||
}
|
||||
|
||||
if (actualChildrenDataBits != childrenDataBits) {
|
||||
if (continueThisLevel && actualChildrenDataBits != childrenDataBits) {
|
||||
// repair the child data mask
|
||||
continueThisLevel = packetData->updatePriorBitMask(childDataBitsPlaceHolder, actualChildrenDataBits);
|
||||
|
||||
if (!continueThisLevel) {
|
||||
qDebug() << "WARNING UNEXPECTED CASE: Failed to update childDataBitsPlaceHolder";
|
||||
qDebug() << "This is not expected!!!! -- continueThisLevel=FALSE....";
|
||||
|
@ -1764,17 +1817,22 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
|||
continueThisLevel = packetData->endLevel(thisLevelKey);
|
||||
} else {
|
||||
packetData->discardLevel(thisLevelKey);
|
||||
qDebug() << "WARNING UNEXPECTED CASE: Something failed in attempting to pack this element";
|
||||
qDebug() << "This is not expected!!!! -- continueThisLevel=FALSE....";
|
||||
|
||||
if (!mustIncludeAllChildData()) {
|
||||
qDebug() << "WARNING UNEXPECTED CASE: Something failed in attempting to pack this element";
|
||||
qDebug() << "This is not expected!!!! -- continueThisLevel=FALSE....";
|
||||
}
|
||||
}
|
||||
|
||||
// This happens if the element could not be written at all. In the case of Octree's that support partial
|
||||
// element data, continueThisLevel will be true. So this only happens if the full element needs to be
|
||||
// added back to the element bag.
|
||||
if (!continueThisLevel) {
|
||||
qDebug() << "WARNING UNEXPECTED CASE - Something failed in attempting to pack this element";
|
||||
qDebug() << "IS THIS EVER EXPECTED???? -- continueThisLevel=FALSE...." ;
|
||||
qDebug() << " calling bag.insert(element);.....";
|
||||
if (!mustIncludeAllChildData()) {
|
||||
qDebug() << "WARNING UNEXPECTED CASE - Something failed in attempting to pack this element.";
|
||||
qDebug() << " If the datatype requires all child data, then this might happen. Otherwise" ;
|
||||
qDebug() << " this is an unexpected case and we should research a potential logic error." ;
|
||||
}
|
||||
|
||||
bag.insert(element);
|
||||
|
||||
|
@ -1826,6 +1884,10 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
|||
|
||||
bool wantImportProgress = true;
|
||||
|
||||
PacketType expectedType = expectedDataPacketType();
|
||||
PacketVersion expectedVersion = versionForPacketType(expectedType);
|
||||
bool hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
|
||||
|
||||
// before reading the file, check to see if this version of the Octree supports file versions
|
||||
if (getWantSVOfileVersions()) {
|
||||
|
||||
|
@ -1840,15 +1902,26 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
|||
unsigned long dataLength = HEADER_LENGTH;
|
||||
|
||||
// if so, read the first byte of the file and see if it matches the expected version code
|
||||
PacketType expectedType = expectedDataPacketType();
|
||||
|
||||
PacketType gotType;
|
||||
memcpy(&gotType, dataAt, sizeof(gotType));
|
||||
|
||||
dataAt += sizeof(expectedType);
|
||||
dataLength -= sizeof(expectedType);
|
||||
gotVersion = *dataAt;
|
||||
|
||||
// NOTE: SPECIAL CASE for old voxel svo files. The old voxel SVO files didn't have header
|
||||
// details. They started with the the octalcode for the root. Which was always 00 which matches PacketTypeUnknown
|
||||
unsigned char* firstByteAt = (unsigned char*)&fileHeader;
|
||||
unsigned char firstByteValue = *firstByteAt;
|
||||
if (expectedType == PacketTypeVoxelData && firstByteValue == 0) {
|
||||
gotType = PacketTypeVoxelData;
|
||||
gotVersion = 0;
|
||||
qDebug() << "Detected OLD Voxels format.";
|
||||
headerLength = 0; // old format files don't have headers
|
||||
file.seekg( 0, std::ios::beg ); // rewind to the beginning so old logic will work
|
||||
}
|
||||
|
||||
if (gotType == expectedType) {
|
||||
dataAt += sizeof(expectedType);
|
||||
dataLength -= sizeof(expectedType);
|
||||
gotVersion = *dataAt;
|
||||
if (canProcessVersion(gotVersion)) {
|
||||
dataAt += sizeof(gotVersion);
|
||||
dataLength -= sizeof(gotVersion);
|
||||
|
@ -1857,25 +1930,25 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
|||
versionForPacketType(expectedDataPacketType()), gotVersion);
|
||||
|
||||
hasBufferBreaks = versionHasSVOfileBreaks(gotVersion);
|
||||
if (hasBufferBreaks) {
|
||||
qDebug() << " this version includes buffer breaks";
|
||||
} else {
|
||||
qDebug() << " this version does not include buffer breaks";
|
||||
}
|
||||
|
||||
} else {
|
||||
qDebug("SVO file version mismatch. Expected: %d Got: %d",
|
||||
versionForPacketType(expectedDataPacketType()), gotVersion);
|
||||
}
|
||||
} else {
|
||||
qDebug("SVO file type mismatch. Expected: %c Got: %c", expectedType, gotType);
|
||||
qDebug() << "SVO file type mismatch. Expected: " << nameForPacketType(expectedType)
|
||||
<< " Got: " << nameForPacketType(gotType);
|
||||
}
|
||||
|
||||
} else {
|
||||
qDebug() << " NOTE: this file type does not include type and version information.";
|
||||
fileOk = true; // assume the file is ok
|
||||
}
|
||||
|
||||
|
||||
if (hasBufferBreaks) {
|
||||
qDebug() << " this version includes buffer breaks";
|
||||
} else {
|
||||
qDebug() << " this version does not include buffer breaks";
|
||||
}
|
||||
|
||||
if (fileOk) {
|
||||
|
||||
|
@ -1906,7 +1979,6 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
|||
quint16 chunkLength = 0;
|
||||
|
||||
file.read((char*)&chunkLength, sizeof(chunkLength)); // read the chunk size from the file
|
||||
|
||||
remainingLength -= sizeof(chunkLength);
|
||||
|
||||
if (chunkLength > remainingLength) {
|
||||
|
@ -1942,6 +2014,7 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
|||
|
||||
file.close();
|
||||
}
|
||||
|
||||
return fileOk;
|
||||
}
|
||||
|
||||
|
@ -1951,25 +2024,25 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
|
|||
if(file.is_open()) {
|
||||
qDebug("Saving to file %s...", fileName);
|
||||
|
||||
bool hasBufferBreaks = false;
|
||||
PacketType expectedType = expectedDataPacketType();
|
||||
PacketVersion expectedVersion = versionForPacketType(expectedType);
|
||||
bool hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
|
||||
|
||||
// before reading the file, check to see if this version of the Octree supports file versions
|
||||
if (getWantSVOfileVersions()) {
|
||||
// if so, read the first byte of the file and see if it matches the expected version code
|
||||
PacketType expectedType = expectedDataPacketType();
|
||||
PacketVersion expectedVersion = versionForPacketType(expectedType);
|
||||
file.write(reinterpret_cast<char*>(&expectedType), sizeof(expectedType));
|
||||
file.write(&expectedVersion, sizeof(expectedVersion));
|
||||
|
||||
qDebug("SVO file type: %c version: %d", expectedType, expectedVersion);
|
||||
qDebug() << "SVO file type: " << nameForPacketType(expectedType) << " version: " << (int)expectedVersion;
|
||||
|
||||
hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
|
||||
if (hasBufferBreaks) {
|
||||
qDebug() << " this version includes buffer breaks";
|
||||
} else {
|
||||
qDebug() << " this version does not include buffer breaks";
|
||||
}
|
||||
}
|
||||
if (hasBufferBreaks) {
|
||||
qDebug() << " this version includes buffer breaks";
|
||||
} else {
|
||||
qDebug() << " this version does not include buffer breaks";
|
||||
}
|
||||
|
||||
|
||||
OctreeElementBag elementBag;
|
||||
OctreeElementExtraEncodeData extraEncodeData;
|
||||
|
|
|
@ -238,6 +238,7 @@ public:
|
|||
virtual int minimumRequiredRootDataBytes() const { return 0; }
|
||||
virtual bool suppressEmptySubtrees() const { return true; }
|
||||
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { }
|
||||
virtual bool mustIncludeAllChildData() const { return true; }
|
||||
|
||||
/// some versions of the SVO file will include breaks with buffer lengths between each buffer chunk in the SVO
|
||||
/// file. If the Octree subclass expects this for this particular version of the file, it should override this
|
||||
|
@ -352,6 +353,7 @@ public:
|
|||
bool getIsClient() const { return !_isServer; } /// Is this a client based tree. Allows guards for certain operations
|
||||
void setIsClient(bool isClient) { _isServer = !isClient; }
|
||||
|
||||
virtual void dumpTree() { };
|
||||
|
||||
signals:
|
||||
void importSize(float x, float y, float z);
|
||||
|
|
|
@ -37,6 +37,8 @@ const float VIEW_FRUSTUM_FOV_OVERSEND = 60.0f;
|
|||
// These are guards to prevent our voxel tree recursive routines from spinning out of control
|
||||
const int UNREASONABLY_DEEP_RECURSION = 20; // use this for something that you want to be shallow, but not spin out
|
||||
const int DANGEROUSLY_DEEP_RECURSION = 200; // use this for something that needs to go deeper
|
||||
const float SCALE_AT_UNREASONABLY_DEEP_RECURSION = (1.0f / powf(2.0f, UNREASONABLY_DEEP_RECURSION));
|
||||
const float SCALE_AT_DANGEROUSLY_DEEP_RECURSION = (1.0f / powf(2.0f, DANGEROUSLY_DEEP_RECURSION));
|
||||
|
||||
const int DEFAULT_MAX_OCTREE_PPS = 600; // the default maximum PPS we think any octree based server should send to a client
|
||||
|
||||
|
|
|
@ -11,11 +11,10 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QRgb>
|
||||
|
||||
|
||||
#include "VoxelTree.h"
|
||||
#include "Tags.h"
|
||||
|
||||
|
@ -567,3 +566,26 @@ int VoxelTree::processEditPacketData(PacketType packetType, const unsigned char*
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class VoxelTreeDebugOperator : public RecurseOctreeOperator {
|
||||
public:
|
||||
virtual bool preRecursion(OctreeElement* element);
|
||||
virtual bool postRecursion(OctreeElement* element) { return true; }
|
||||
};
|
||||
|
||||
bool VoxelTreeDebugOperator::preRecursion(OctreeElement* element) {
|
||||
VoxelTreeElement* treeElement = static_cast<VoxelTreeElement*>(element);
|
||||
qDebug() << "VoxelTreeElement [" << treeElement << ":" << treeElement->getAACube() << "]";
|
||||
qDebug() << " isLeaf:" << treeElement->isLeaf();
|
||||
qDebug() << " color:" << treeElement->getColor()[0] << ", "
|
||||
<< treeElement->getColor()[1] << ", "
|
||||
<< treeElement->getColor()[2];
|
||||
return true;
|
||||
}
|
||||
|
||||
void VoxelTree::dumpTree() {
|
||||
// First, look for the existing entity in the tree..
|
||||
VoxelTreeDebugOperator theOperator;
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,12 +51,30 @@ public:
|
|||
|
||||
void readCodeColorBufferToTree(const unsigned char* codeColorBuffer, bool destructive = false);
|
||||
|
||||
|
||||
virtual bool getWantSVOfileVersions() const { return true; }
|
||||
virtual bool canProcessVersion(PacketVersion thisVersion) const {
|
||||
return thisVersion == 0 || thisVersion == versionForPacketType(expectedDataPacketType()); }
|
||||
virtual PacketVersion expectedVersion() const { return versionForPacketType(expectedDataPacketType()); }
|
||||
|
||||
virtual PacketType expectedDataPacketType() const { return PacketTypeVoxelData; }
|
||||
virtual bool handlesEditPacketType(PacketType packetType) const;
|
||||
virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
|
||||
const unsigned char* editData, int maxLength, const SharedNodePointer& node);
|
||||
virtual bool recurseChildrenWithData() const { return false; }
|
||||
|
||||
/// some versions of the SVO file will include breaks with buffer lengths between each buffer chunk in the SVO
|
||||
/// file. If the Octree subclass expects this for this particular version of the file, it should override this
|
||||
/// method and return true.
|
||||
virtual bool versionHasSVOfileBreaks(PacketVersion thisVersion) const {
|
||||
if (thisVersion == 0) {
|
||||
return false; // old versions didn't have buffer breaks
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void dumpTree();
|
||||
|
||||
private:
|
||||
// helper functions for nudgeSubTree
|
||||
void recurseNodeForNudge(VoxelTreeElement* element, RecurseOctreeOperation operation, void* extraData);
|
||||
|
|
|
@ -73,6 +73,13 @@ OctreeElement::AppendState VoxelTreeElement::appendElementData(OctreePacketData*
|
|||
int VoxelTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args) {
|
||||
const int BYTES_PER_COLOR = 3;
|
||||
|
||||
if (bytesLeftToRead < BYTES_PER_COLOR) {
|
||||
qDebug() << "UNEXPECTED: readElementDataFromBuffer() only had " << bytesLeftToRead << " bytes. "
|
||||
"Not enough for meaningful data.";
|
||||
return bytesLeftToRead;
|
||||
}
|
||||
|
||||
|
||||
// pull the color for this child
|
||||
nodeColor newColor = { 128, 128, 128, 1};
|
||||
|
|
Loading…
Reference in a new issue