mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 14:44:39 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
Conflicts: interface/src/avatar/Avatar.cpp
This commit is contained in:
commit
ff245427fd
16 changed files with 1952 additions and 1796 deletions
|
@ -45,6 +45,7 @@
|
|||
#include "VoxelImporter.h"
|
||||
#include "Webcam.h"
|
||||
#include "avatar/Avatar.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "avatar/HandControl.h"
|
||||
#include "renderer/AmbientOcclusionEffect.h"
|
||||
#include "renderer/GeometryCache.h"
|
||||
|
@ -106,7 +107,7 @@ public:
|
|||
const glm::vec3 getMouseVoxelWorldCoordinates(const VoxelDetail _mouseVoxel);
|
||||
|
||||
QGLWidget* getGLWidget() { return _glWidget; }
|
||||
Avatar* getAvatar() { return &_myAvatar; }
|
||||
MyAvatar* getAvatar() { return &_myAvatar; }
|
||||
Audio* getAudio() { return &_audio; }
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
|
@ -249,7 +250,7 @@ private:
|
|||
|
||||
Oscilloscope _audioScope;
|
||||
|
||||
Avatar _myAvatar; // The rendered avatar of oneself
|
||||
MyAvatar _myAvatar; // The rendered avatar of oneself
|
||||
|
||||
Transmitter _myTransmitter; // Gets UDP data from transmitter app used to animate the avatar
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,36 +27,41 @@
|
|||
#include "world.h"
|
||||
|
||||
|
||||
static const float MAX_SCALE = 1000.f;
|
||||
static const float MIN_SCALE = .005f;
|
||||
static const float SCALING_RATIO = .05f;
|
||||
static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1
|
||||
static const float MAX_SCALE = 1000.f;
|
||||
static const float MIN_SCALE = .005f;
|
||||
static const float SCALING_RATIO = .05f;
|
||||
static const float SMOOTHING_RATIO = .05f; // 0 < ratio < 1
|
||||
static const float RESCALING_TOLERANCE = .02f;
|
||||
|
||||
const float BODY_BALL_RADIUS_PELVIS = 0.07;
|
||||
const float BODY_BALL_RADIUS_TORSO = 0.065;
|
||||
const float BODY_BALL_RADIUS_CHEST = 0.08;
|
||||
const float BODY_BALL_RADIUS_NECK_BASE = 0.03;
|
||||
const float BODY_BALL_RADIUS_HEAD_BASE = 0.07;
|
||||
const float BODY_BALL_RADIUS_LEFT_COLLAR = 0.04;
|
||||
const float BODY_BALL_RADIUS_LEFT_SHOULDER = 0.03;
|
||||
const float BODY_BALL_RADIUS_LEFT_ELBOW = 0.02;
|
||||
const float BODY_BALL_RADIUS_LEFT_WRIST = 0.02;
|
||||
const float BODY_BALL_RADIUS_LEFT_FINGERTIPS = 0.01;
|
||||
const float BODY_BALL_RADIUS_RIGHT_COLLAR = 0.04;
|
||||
const float BODY_BALL_RADIUS_RIGHT_SHOULDER = 0.03;
|
||||
const float BODY_BALL_RADIUS_RIGHT_ELBOW = 0.02;
|
||||
const float BODY_BALL_RADIUS_RIGHT_WRIST = 0.02;
|
||||
const float BODY_BALL_RADIUS_PELVIS = 0.07;
|
||||
const float BODY_BALL_RADIUS_TORSO = 0.065;
|
||||
const float BODY_BALL_RADIUS_CHEST = 0.08;
|
||||
const float BODY_BALL_RADIUS_NECK_BASE = 0.03;
|
||||
const float BODY_BALL_RADIUS_HEAD_BASE = 0.07;
|
||||
const float BODY_BALL_RADIUS_LEFT_COLLAR = 0.04;
|
||||
const float BODY_BALL_RADIUS_LEFT_SHOULDER = 0.03;
|
||||
const float BODY_BALL_RADIUS_LEFT_ELBOW = 0.02;
|
||||
const float BODY_BALL_RADIUS_LEFT_WRIST = 0.02;
|
||||
const float BODY_BALL_RADIUS_LEFT_FINGERTIPS = 0.01;
|
||||
const float BODY_BALL_RADIUS_RIGHT_COLLAR = 0.04;
|
||||
const float BODY_BALL_RADIUS_RIGHT_SHOULDER = 0.03;
|
||||
const float BODY_BALL_RADIUS_RIGHT_ELBOW = 0.02;
|
||||
const float BODY_BALL_RADIUS_RIGHT_WRIST = 0.02;
|
||||
const float BODY_BALL_RADIUS_RIGHT_FINGERTIPS = 0.01;
|
||||
const float BODY_BALL_RADIUS_LEFT_HIP = 0.04;
|
||||
const float BODY_BALL_RADIUS_LEFT_MID_THIGH = 0.03;
|
||||
const float BODY_BALL_RADIUS_LEFT_KNEE = 0.025;
|
||||
const float BODY_BALL_RADIUS_LEFT_HEEL = 0.025;
|
||||
const float BODY_BALL_RADIUS_LEFT_TOES = 0.025;
|
||||
const float BODY_BALL_RADIUS_RIGHT_HIP = 0.04;
|
||||
const float BODY_BALL_RADIUS_RIGHT_KNEE = 0.025;
|
||||
const float BODY_BALL_RADIUS_RIGHT_HEEL = 0.025;
|
||||
const float BODY_BALL_RADIUS_RIGHT_TOES = 0.025;
|
||||
const float BODY_BALL_RADIUS_LEFT_HIP = 0.04;
|
||||
const float BODY_BALL_RADIUS_LEFT_MID_THIGH = 0.03;
|
||||
const float BODY_BALL_RADIUS_LEFT_KNEE = 0.025;
|
||||
const float BODY_BALL_RADIUS_LEFT_HEEL = 0.025;
|
||||
const float BODY_BALL_RADIUS_LEFT_TOES = 0.025;
|
||||
const float BODY_BALL_RADIUS_RIGHT_HIP = 0.04;
|
||||
const float BODY_BALL_RADIUS_RIGHT_KNEE = 0.025;
|
||||
const float BODY_BALL_RADIUS_RIGHT_HEEL = 0.025;
|
||||
const float BODY_BALL_RADIUS_RIGHT_TOES = 0.025;
|
||||
|
||||
extern const bool usingBigSphereCollisionTest;
|
||||
|
||||
extern const float chatMessageScale;
|
||||
extern const float chatMessageHeight;
|
||||
|
||||
enum AvatarBodyBallID {
|
||||
BODY_BALL_NULL = -1,
|
||||
|
@ -84,9 +89,6 @@ enum AvatarBodyBallID {
|
|||
BODY_BALL_RIGHT_KNEE,
|
||||
BODY_BALL_RIGHT_HEEL,
|
||||
BODY_BALL_RIGHT_TOES,
|
||||
|
||||
//TEST!
|
||||
//BODY_BALL_LEFT_MID_THIGH,
|
||||
NUM_AVATAR_BODY_BALLS
|
||||
};
|
||||
|
||||
|
@ -117,6 +119,8 @@ enum ScreenTintLayer {
|
|||
NUM_SCREEN_TINT_LAYERS
|
||||
};
|
||||
|
||||
class MyAvatar;
|
||||
|
||||
// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found)
|
||||
// this is basically in the center of the ground plane. Slightly adjusted. This was asked for by
|
||||
// Grayson as he's building a street around here for demo dinner 2
|
||||
|
@ -124,6 +128,7 @@ const glm::vec3 START_LOCATION(0.485f * TREE_SCALE, 0.f, 0.5f * TREE_SCALE);
|
|||
|
||||
class Avatar : public AvatarData {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void sendAvatarVoxelURLMessage(const QUrl& url);
|
||||
|
||||
|
@ -131,84 +136,32 @@ public:
|
|||
~Avatar();
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity);
|
||||
void updateThrust(float deltaTime, Transmitter * transmitter);
|
||||
void follow(Avatar* leadingAvatar);
|
||||
void updateFromGyrosAndOrWebcam(bool gyroLook,
|
||||
float pitchFromTouch);
|
||||
void addBodyYaw(float bodyYaw) {_bodyYaw += bodyYaw;};
|
||||
void addBodyYawDelta(float bodyYawDelta) {_bodyYawDelta += bodyYawDelta;}
|
||||
void render(bool lookingInMirror, bool renderAvatarBalls);
|
||||
void renderScreenTint(ScreenTintLayer layer, Camera& whichCamera);
|
||||
|
||||
//setters
|
||||
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
|
||||
void setMovedHandOffset(glm::vec3 movedHandOffset) { _movedHandOffset = movedHandOffset;}
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
void setDisplayingLookatVectors(bool displayingLookatVectors) { _head.setRenderLookatVectors(displayingLookatVectors); }
|
||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; };
|
||||
void setLeanScale(float scale) { _leanScale = scale;}
|
||||
|
||||
void setGravity(glm::vec3 gravity);
|
||||
void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction);
|
||||
void setOrientation(const glm::quat& orientation);
|
||||
void setNewScale(const float scale);
|
||||
|
||||
//getters
|
||||
bool isInitialized() const { return _initialized;}
|
||||
bool isMyAvatar() const { return _owningNode == NULL; }
|
||||
const Skeleton& getSkeleton() const { return _skeleton;}
|
||||
float getHeadYawRate() const { return _head.yawRate;}
|
||||
float getBodyYaw() const { return _bodyYaw;}
|
||||
bool getIsNearInteractingOther() const { return _avatarTouch.getAbleToReachOtherAvatar();}
|
||||
const glm::vec3& getHeadJointPosition() const { return _skeleton.joint[ AVATAR_JOINT_HEAD_BASE ].position;}
|
||||
const glm::vec3& getBallPosition(AvatarJointID j) const { return _bodyBall[j].position;}
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
float getScale() const { return _scale;}
|
||||
float getNewScale() const { return _newScale;}
|
||||
const glm::vec3& getVelocity() const { return _velocity;}
|
||||
float getSpeed() const { return _speed;}
|
||||
float getHeight() const { return _height;}
|
||||
AvatarMode getMode() const { return _mode;}
|
||||
float getLeanScale() const { return _leanScale;}
|
||||
float getElapsedTimeStopped() const { return _elapsedTimeStopped;}
|
||||
float getElapsedTimeMoving() const { return _elapsedTimeMoving;}
|
||||
float getElapsedTimeSinceCollision() const { return _elapsedTimeSinceCollision;}
|
||||
const glm::vec3& getLastCollisionPosition() const { return _lastCollisionPosition;}
|
||||
float getAbsoluteHeadYaw() const;
|
||||
float getAbsoluteHeadPitch() const;
|
||||
bool isInitialized() const { return _initialized; }
|
||||
const Skeleton& getSkeleton() const { return _skeleton; }
|
||||
float getHeadYawRate() const { return _head.yawRate; }
|
||||
const glm::vec3& getHeadJointPosition() const { return _skeleton.joint[ AVATAR_JOINT_HEAD_BASE ].position; }
|
||||
float getScale() const { return _scale; }
|
||||
const glm::vec3& getVelocity() const { return _velocity; }
|
||||
Head& getHead() {return _head; }
|
||||
Hand& getHand() {return _hand; }
|
||||
glm::quat getOrientation() const;
|
||||
glm::quat getWorldAlignedOrientation() const;
|
||||
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
|
||||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
Avatar* getLeadingAvatar() const { return _leadingAvatar; }
|
||||
glm::vec3 getGravity() const { return _gravity; }
|
||||
|
||||
glm::vec3 getUprightHeadPosition() const;
|
||||
glm::vec3 getUprightEyeLevelPosition() const;
|
||||
glm::vec3 getEyePosition();
|
||||
|
||||
AvatarVoxelSystem* getVoxels() { return &_voxels; }
|
||||
|
||||
// Set what driving keys are being pressed to control thrust levels
|
||||
void setDriveKeys(int key, bool val) { _driveKeys[key] = val; };
|
||||
bool getDriveKeys(int key) { return _driveKeys[key]; };
|
||||
void jump() { _shouldJump = true; };
|
||||
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
// get/set avatar data
|
||||
void saveData(QSettings* set);
|
||||
void loadData(QSettings* set);
|
||||
|
||||
// Get the position/rotation of a single body ball
|
||||
// Get the position/rotation of a single body ball
|
||||
void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const;
|
||||
|
||||
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
|
||||
|
@ -220,96 +173,80 @@ public slots:
|
|||
void decreaseSize();
|
||||
void resetSize();
|
||||
|
||||
friend class MyAvatar;
|
||||
|
||||
|
||||
protected:
|
||||
struct AvatarBall {
|
||||
AvatarJointID parentJoint; /// the skeletal joint that serves as a reference for determining the position
|
||||
glm::vec3 parentOffset; /// a 3D vector in the frame of reference of the parent skeletal joint
|
||||
AvatarBodyBallID parentBall; /// the ball to which this ball is constrained for spring forces
|
||||
glm::vec3 position; /// the actual dynamic position of the ball at any given time
|
||||
glm::quat rotation; /// the rotation of the ball
|
||||
glm::vec3 velocity; /// the velocity of the ball
|
||||
float springLength; /// the ideal length of the spring between this ball and its parentBall
|
||||
float jointTightness; /// how tightly the ball position attempts to stay at its ideal position (determined by parentOffset)
|
||||
float radius; /// the radius of the ball
|
||||
bool isCollidable; /// whether or not the ball responds to collisions
|
||||
float touchForce; /// a scalar determining the amount that the cursor (or hand) is penetrating the ball
|
||||
};
|
||||
|
||||
Head _head;
|
||||
Hand _hand;
|
||||
Skeleton _skeleton;
|
||||
bool _ballSpringsInitialized;
|
||||
float _TEST_bigSphereRadius;
|
||||
glm::vec3 _TEST_bigSpherePosition;
|
||||
float _bodyYawDelta;
|
||||
glm::vec3 _movedHandOffset;
|
||||
AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ];
|
||||
AvatarMode _mode;
|
||||
glm::vec3 _velocity;
|
||||
glm::vec3 _thrust;
|
||||
float _speed;
|
||||
float _leanScale;
|
||||
float _pelvisFloatingHeight;
|
||||
float _pelvisToHeadLength;
|
||||
float _scale;
|
||||
float _height;
|
||||
Balls* _balls;
|
||||
AvatarTouch _avatarTouch;
|
||||
glm::vec3 _worldUpDirection;
|
||||
glm::vec3 _mouseRayOrigin;
|
||||
glm::vec3 _mouseRayDirection;
|
||||
bool _isCollisionsOn;
|
||||
Avatar* _leadingAvatar;
|
||||
float _stringLength;
|
||||
AvatarVoxelSystem _voxels;
|
||||
|
||||
// protected methods...
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void updateCollisionWithSphere(glm::vec3 position, float radius, float deltaTime);
|
||||
void updateBodyBalls(float deltaTime);
|
||||
void updateArmIKAndConstraints(float deltaTime);
|
||||
void setScale(const float scale);
|
||||
|
||||
|
||||
private:
|
||||
// privatize copy constructor and assignment operator to avoid copying
|
||||
Avatar(const Avatar&);
|
||||
Avatar& operator= (const Avatar&);
|
||||
|
||||
struct AvatarBall {
|
||||
AvatarJointID parentJoint; // the skeletal joint that serves as a reference for determining the position
|
||||
glm::vec3 parentOffset; // a 3D vector in the frame of reference of the parent skeletal joint
|
||||
AvatarBodyBallID parentBall; // the ball to which this ball is constrained for spring forces
|
||||
glm::vec3 position; // the actual dynamic position of the ball at any given time
|
||||
glm::quat rotation; // the rotation of the ball
|
||||
glm::vec3 velocity; // the velocity of the ball
|
||||
float springLength; // the ideal length of the spring between this ball and its parentBall
|
||||
float jointTightness; // how tightly the ball position attempts to stay at its ideal position (determined by parentOffset)
|
||||
float radius; // the radius of the ball
|
||||
bool isCollidable; // whether or not the ball responds to collisions
|
||||
float touchForce; // a scalar determining the amount that the cursor (or hand) is penetrating the ball
|
||||
};
|
||||
|
||||
bool _initialized;
|
||||
Head _head;
|
||||
Hand _hand;
|
||||
Skeleton _skeleton;
|
||||
bool _ballSpringsInitialized;
|
||||
float _TEST_bigSphereRadius;
|
||||
glm::vec3 _TEST_bigSpherePosition;
|
||||
bool _mousePressed;
|
||||
float _bodyPitchDelta;
|
||||
float _bodyYawDelta;
|
||||
float _bodyRollDelta;
|
||||
glm::vec3 _movedHandOffset;
|
||||
AvatarBall _bodyBall[ NUM_AVATAR_BODY_BALLS ];
|
||||
AvatarMode _mode;
|
||||
glm::vec3 _handHoldingPosition;
|
||||
glm::vec3 _velocity;
|
||||
glm::vec3 _thrust;
|
||||
bool _shouldJump;
|
||||
float _speed;
|
||||
float _maxArmLength;
|
||||
float _leanScale;
|
||||
int _driveKeys[MAX_DRIVE_KEYS];
|
||||
float _pelvisStandingHeight;
|
||||
float _pelvisFloatingHeight;
|
||||
float _pelvisToHeadLength;
|
||||
float _scale;
|
||||
float _height;
|
||||
Balls* _balls;
|
||||
AvatarTouch _avatarTouch;
|
||||
float _distanceToNearestAvatar; // How close is the nearest avatar?
|
||||
glm::vec3 _gravity;
|
||||
glm::vec3 _worldUpDirection;
|
||||
glm::vec3 _mouseRayOrigin;
|
||||
glm::vec3 _mouseRayDirection;
|
||||
Avatar* _interactingOther;
|
||||
bool _isMouseTurningRight;
|
||||
float _elapsedTimeMoving; // Timers to drive camera transitions when moving
|
||||
float _elapsedTimeStopped;
|
||||
float _elapsedTimeSinceCollision;
|
||||
glm::vec3 _lastCollisionPosition;
|
||||
bool _speedBrakes;
|
||||
bool _isThrustOn;
|
||||
bool _isCollisionsOn;
|
||||
float _collisionRadius;
|
||||
|
||||
Avatar* _leadingAvatar;
|
||||
float _stringLength;
|
||||
|
||||
AvatarVoxelSystem _voxels;
|
||||
bool _initialized;
|
||||
glm::vec3 _handHoldingPosition;
|
||||
float _maxArmLength;
|
||||
float _pelvisStandingHeight;
|
||||
|
||||
// private methods...
|
||||
glm::vec3 calculateAverageEyePosition() { return _head.calculateAverageEyePosition(); } // get the position smack-dab between the eyes (for lookat)
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
float getBallRenderAlpha(int ball, bool lookingInMirror) const;
|
||||
void renderBody(bool lookingInMirror, bool renderAvatarBalls);
|
||||
void initializeBodyBalls();
|
||||
void resetBodyBalls();
|
||||
void updateBodyBalls( float deltaTime );
|
||||
void calculateBoneLengths();
|
||||
void readSensors();
|
||||
void updateHandMovementAndTouching(float deltaTime, bool enableHandMovement);
|
||||
void updateAvatarCollisions(float deltaTime);
|
||||
void updateArmIKAndConstraints( float deltaTime );
|
||||
void updateCollisionWithSphere( glm::vec3 position, float radius, float deltaTime );
|
||||
void updateCollisionWithEnvironment(float deltaTime);
|
||||
void updateCollisionWithVoxels(float deltaTime);
|
||||
void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping);
|
||||
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||
void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
|
||||
void checkForMouseRayTouching();
|
||||
void setScale (const float scale);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -36,7 +36,7 @@ Hand::Hand(Avatar* owningAvatar) :
|
|||
|
||||
void Hand::init() {
|
||||
// Different colors for my hand and others' hands
|
||||
if (_owningAvatar && _owningAvatar->isMyAvatar()) {
|
||||
if (_owningAvatar && _owningAvatar->getOwningNode() == NULL) {
|
||||
_ballColor = glm::vec3(0.0, 0.4, 0.0);
|
||||
}
|
||||
else {
|
||||
|
|
1035
interface/src/avatar/MyAvatar.cpp
Normal file
1035
interface/src/avatar/MyAvatar.cpp
Normal file
File diff suppressed because it is too large
Load diff
90
interface/src/avatar/MyAvatar.h
Normal file
90
interface/src/avatar/MyAvatar.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// MyAvatar.h
|
||||
// interface
|
||||
//
|
||||
// Created by Mark Peng on 8/16/13.
|
||||
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __interface__myavatar__
|
||||
#define __interface__myavatar__
|
||||
|
||||
#include "Avatar.h"
|
||||
|
||||
class MyAvatar : public Avatar {
|
||||
public:
|
||||
MyAvatar(Node* owningNode = NULL);
|
||||
|
||||
void reset();
|
||||
void simulate(float deltaTime, Transmitter* transmitter, float gyroCameraSensitivity);
|
||||
void updateFromGyrosAndOrWebcam(bool gyroLook, float pitchFromTouch);
|
||||
void render(bool lookingInMirror, bool renderAvatarBalls);
|
||||
void renderScreenTint(ScreenTintLayer layer, Camera& whichCamera);
|
||||
|
||||
// setters
|
||||
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
|
||||
void setMovedHandOffset(glm::vec3 movedHandOffset) { _movedHandOffset = movedHandOffset; }
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
||||
void setLeanScale(float scale) { _leanScale = scale; }
|
||||
void setGravity(glm::vec3 gravity);
|
||||
void setOrientation(const glm::quat& orientation);
|
||||
void setNewScale(const float scale);
|
||||
void setWantCollisionsOn(bool wantCollisionsOn) { _isCollisionsOn = wantCollisionsOn; }
|
||||
|
||||
// getters
|
||||
float getNewScale() const { return _newScale; }
|
||||
float getSpeed() const { return _speed; }
|
||||
AvatarMode getMode() const { return _mode; }
|
||||
float getLeanScale() const { return _leanScale; }
|
||||
float getElapsedTimeStopped() const { return _elapsedTimeStopped; }
|
||||
float getElapsedTimeMoving() const { return _elapsedTimeMoving; }
|
||||
float getAbsoluteHeadYaw() const;
|
||||
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
|
||||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
Avatar* getLeadingAvatar() const { return _leadingAvatar; }
|
||||
glm::vec3 getGravity() const { return _gravity; }
|
||||
glm::vec3 getUprightHeadPosition() const;
|
||||
glm::vec3 getUprightEyeLevelPosition() const;
|
||||
|
||||
// Set what driving keys are being pressed to control thrust levels
|
||||
void setDriveKeys(int key, bool val) { _driveKeys[key] = val; };
|
||||
bool getDriveKeys(int key) { return _driveKeys[key]; };
|
||||
void jump() { _shouldJump = true; };
|
||||
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
private:
|
||||
bool _mousePressed;
|
||||
float _bodyPitchDelta;
|
||||
float _bodyRollDelta;
|
||||
bool _shouldJump;
|
||||
int _driveKeys[MAX_DRIVE_KEYS];
|
||||
glm::vec3 _gravity;
|
||||
float _distanceToNearestAvatar; // How close is the nearest avatar?
|
||||
Avatar* _interactingOther;
|
||||
float _elapsedTimeMoving; // Timers to drive camera transitions when moving
|
||||
float _elapsedTimeStopped;
|
||||
float _elapsedTimeSinceCollision;
|
||||
glm::vec3 _lastCollisionPosition;
|
||||
bool _speedBrakes;
|
||||
bool _isThrustOn;
|
||||
float _collisionRadius;
|
||||
|
||||
// private methods
|
||||
float getBallRenderAlpha(int ball, bool lookingInMirror) const;
|
||||
void renderBody(bool lookingInMirror, bool renderAvatarBalls);
|
||||
void updateThrust(float deltaTime, Transmitter * transmitter);
|
||||
void updateHandMovementAndTouching(float deltaTime, bool enableHandMovement);
|
||||
void updateAvatarCollisions(float deltaTime);
|
||||
void updateCollisionWithEnvironment(float deltaTime);
|
||||
void updateCollisionWithVoxels(float deltaTime);
|
||||
void applyHardCollision(const glm::vec3& penetration, float elasticity, float damping);
|
||||
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
|
||||
void applyCollisionWithOtherAvatar( Avatar * other, float deltaTime );
|
||||
void checkForMouseRayTouching();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,242 +0,0 @@
|
|||
//
|
||||
// SquarePixelMap.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Tomáš Horáček on 6/25/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SquarePixelMap.h"
|
||||
|
||||
#define CHILD_COORD_X_IS_1 0x1
|
||||
#define CHILD_COORD_Y_IS_1 0x2
|
||||
#define ALPHA_CHANNEL_RANGE_FLOAT 256.f
|
||||
#define ALPHA_CHANNEL_BIT_OFFSET 24
|
||||
#define RED_CHANNEL_BIT_OFFSET 16
|
||||
#define GREEN_CHANNEL_BIT_OFFSET 8
|
||||
|
||||
unsigned int numberOfBitsForSize(unsigned int size) {
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size--;
|
||||
|
||||
unsigned int ans = 1;
|
||||
while (size >>= 1) {
|
||||
ans++;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
struct PixelQuadTreeCoordinates {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
class PixelQuadTreeNode {
|
||||
public:
|
||||
PixelQuadTreeCoordinates _coord;
|
||||
uint32_t _color; // undefined value for _allChildrenHasSameColor = false
|
||||
bool _allChildrenHasSameColor;
|
||||
uint8_t _minimumNeighbourhoodAplha;
|
||||
|
||||
// 0 x -> 1
|
||||
// +---+---+
|
||||
// y | 0 | 1 | <- child index
|
||||
// | +---+---+
|
||||
// v | 2 | 3 |
|
||||
// +---+---+
|
||||
// 1
|
||||
PixelQuadTreeNode* _children[4];
|
||||
|
||||
PixelQuadTreeNode(PixelQuadTreeCoordinates coord, SquarePixelMap* pixelMap);
|
||||
~PixelQuadTreeNode() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
delete _children[i];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void updateChildCoordinates(int i, PixelQuadTreeCoordinates& childCoord) {
|
||||
childCoord.x = _coord.x;
|
||||
childCoord.y = _coord.y;
|
||||
|
||||
if (i & CHILD_COORD_X_IS_1) {
|
||||
childCoord.x += childCoord.size;
|
||||
}
|
||||
if (i & CHILD_COORD_Y_IS_1) {
|
||||
childCoord.y += childCoord.size;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasAllChildrenSameColor() {
|
||||
return false; //turn off import voxel grouping
|
||||
|
||||
for (int i = 1; i < 4; i++) {
|
||||
if (!_children[i]->_allChildrenHasSameColor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t firstColor = _children[0]->_color;
|
||||
|
||||
for (int i = 1; i < 4; i++) {
|
||||
if (firstColor != _children[i]->_color) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
PixelQuadTreeNode::PixelQuadTreeNode(PixelQuadTreeCoordinates coord, SquarePixelMap* pixelMap) : _coord(coord), _minimumNeighbourhoodAplha(-1) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
_children[i] = NULL;
|
||||
}
|
||||
|
||||
if (_coord.size == 1) {
|
||||
_color = pixelMap->getPixelAt(_coord.x, _coord.y);
|
||||
|
||||
_minimumNeighbourhoodAplha = std::min<uint8_t>(pixelMap->getAlphaAt(_coord.x + 1, _coord.y), _minimumNeighbourhoodAplha);
|
||||
_minimumNeighbourhoodAplha = std::min<uint8_t>(pixelMap->getAlphaAt(_coord.x - 1, _coord.y), _minimumNeighbourhoodAplha);
|
||||
_minimumNeighbourhoodAplha = std::min<uint8_t>(pixelMap->getAlphaAt(_coord.x, _coord.y + 1), _minimumNeighbourhoodAplha);
|
||||
_minimumNeighbourhoodAplha = std::min<uint8_t>(pixelMap->getAlphaAt(_coord.x, _coord.y - 1), _minimumNeighbourhoodAplha);
|
||||
|
||||
_allChildrenHasSameColor = true;
|
||||
} else {
|
||||
PixelQuadTreeCoordinates childCoord = PixelQuadTreeCoordinates();
|
||||
childCoord.size = _coord.size / 2;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
this->updateChildCoordinates(i, childCoord);
|
||||
|
||||
|
||||
if (childCoord.x < pixelMap->dimension() &&
|
||||
childCoord.y < pixelMap->dimension()) {
|
||||
|
||||
_children[i] = new PixelQuadTreeNode(childCoord, pixelMap);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->hasAllChildrenSameColor()) {
|
||||
_allChildrenHasSameColor = true;
|
||||
_color = _children[0]->_color;
|
||||
|
||||
_minimumNeighbourhoodAplha = _children[0]->_minimumNeighbourhoodAplha;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
_minimumNeighbourhoodAplha = std::min<uint8_t>(_children[i]->_minimumNeighbourhoodAplha, _minimumNeighbourhoodAplha);
|
||||
delete _children[i];
|
||||
_children[i] = NULL;
|
||||
}
|
||||
} else {
|
||||
_allChildrenHasSameColor = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SquarePixelMap::SquarePixelMap(const uint32_t* pixels, int dimension) : _rootPixelQuadTreeNode(NULL) {
|
||||
_data = new SquarePixelMapData();
|
||||
_data->dimension = dimension;
|
||||
_data->reference_counter = 1;
|
||||
|
||||
size_t pixels_size = dimension * dimension;
|
||||
_data->pixels = new uint32_t[pixels_size];
|
||||
memcpy((void*)_data->pixels, (void*)pixels, sizeof(uint32_t) * pixels_size);
|
||||
}
|
||||
|
||||
SquarePixelMap::SquarePixelMap(const SquarePixelMap& other) {
|
||||
this->_data = other._data;
|
||||
this->_data->reference_counter++;
|
||||
}
|
||||
|
||||
SquarePixelMap::~SquarePixelMap() {
|
||||
delete _rootPixelQuadTreeNode;
|
||||
|
||||
if (--_data->reference_counter == 0) {
|
||||
delete _data->pixels;
|
||||
delete _data;
|
||||
}
|
||||
}
|
||||
|
||||
void SquarePixelMap::addVoxelsToVoxelTree(VoxelTree* voxelTree) {
|
||||
this->generateRootPixelQuadTreeNode();
|
||||
this->createVoxelsFromPixelQuadTreeToVoxelTree(_rootPixelQuadTreeNode, voxelTree);
|
||||
}
|
||||
|
||||
int SquarePixelMap::dimension() {
|
||||
return _data->dimension;
|
||||
}
|
||||
|
||||
uint32_t SquarePixelMap::getPixelAt(unsigned int x, unsigned int y) {
|
||||
return _data->pixels[x + y * _data->dimension];
|
||||
}
|
||||
|
||||
uint8_t SquarePixelMap::getAlphaAt(int x, int y) {
|
||||
int max_coord = this->dimension() - 1;
|
||||
|
||||
if (x < 0 || y < 0 || x > max_coord || y > max_coord) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this->getPixelAt(x, y) >> ALPHA_CHANNEL_BIT_OFFSET;
|
||||
}
|
||||
|
||||
void SquarePixelMap::generateRootPixelQuadTreeNode() {
|
||||
delete _rootPixelQuadTreeNode;
|
||||
|
||||
PixelQuadTreeCoordinates rootNodeCoord = PixelQuadTreeCoordinates();
|
||||
rootNodeCoord.size = 1 << numberOfBitsForSize(_data->dimension);
|
||||
rootNodeCoord.x = rootNodeCoord.y = 0;
|
||||
|
||||
_rootPixelQuadTreeNode = new PixelQuadTreeNode(rootNodeCoord, this);
|
||||
}
|
||||
|
||||
void SquarePixelMap::createVoxelsFromPixelQuadTreeToVoxelTree(PixelQuadTreeNode* pixelQuadTreeNode, VoxelTree* voxelTree) {
|
||||
if (pixelQuadTreeNode->_allChildrenHasSameColor) {
|
||||
VoxelDetail voxel = this->getVoxelDetail(pixelQuadTreeNode);
|
||||
|
||||
unsigned char minimumNeighbourhoodAplha = std::max<int>(0, pixelQuadTreeNode->_minimumNeighbourhoodAplha - 1);
|
||||
|
||||
float minimumNeighbourhoodY = voxel.s * (floor(minimumNeighbourhoodAplha / (ALPHA_CHANNEL_RANGE_FLOAT * voxel.s)) + 0.5);
|
||||
|
||||
do {
|
||||
voxelTree->createVoxel(voxel.x, voxel.y, voxel.z, voxel.s, voxel.red, voxel.green, voxel.blue, true);
|
||||
} while ((voxel.y -= voxel.s) > minimumNeighbourhoodY);
|
||||
} else {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
PixelQuadTreeNode* child = pixelQuadTreeNode->_children[i];
|
||||
if (child) {
|
||||
this->createVoxelsFromPixelQuadTreeToVoxelTree(child, voxelTree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VoxelDetail SquarePixelMap::getVoxelDetail(PixelQuadTreeNode* pixelQuadTreeNode) {
|
||||
VoxelDetail voxel = VoxelDetail();
|
||||
|
||||
uint32_t color = pixelQuadTreeNode->_color;
|
||||
unsigned char alpha = std::max<int>(0, (color >> ALPHA_CHANNEL_BIT_OFFSET) - 1);
|
||||
|
||||
voxel.red = color >> RED_CHANNEL_BIT_OFFSET;
|
||||
voxel.green = color >> GREEN_CHANNEL_BIT_OFFSET;
|
||||
voxel.blue = color;
|
||||
|
||||
|
||||
float rootSize = _rootPixelQuadTreeNode->_coord.size;
|
||||
|
||||
voxel.s = pixelQuadTreeNode->_coord.size / rootSize;
|
||||
voxel.y = voxel.s * (floor(alpha / (ALPHA_CHANNEL_RANGE_FLOAT * voxel.s)) + 0.5);
|
||||
voxel.x = pixelQuadTreeNode->_coord.x / rootSize + voxel.s / 2;
|
||||
voxel.z = pixelQuadTreeNode->_coord.y / rootSize + voxel.s / 2;
|
||||
|
||||
return voxel;
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
//
|
||||
// SquarePixelMap.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Tomáš Horáček on 6/25/13.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __hifi__SquarePixelMap__
|
||||
#define __hifi__SquarePixelMap__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "VoxelTree.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
class PixelQuadTreeNode;
|
||||
|
||||
struct SquarePixelMapData {
|
||||
const uint32_t* pixels;
|
||||
int dimension;
|
||||
int reference_counter;
|
||||
};
|
||||
|
||||
class SquarePixelMap {
|
||||
public:
|
||||
SquarePixelMap(const uint32_t* pixels, int dimension);
|
||||
SquarePixelMap(const SquarePixelMap& other);
|
||||
~SquarePixelMap();
|
||||
|
||||
void addVoxelsToVoxelTree(VoxelTree* voxelTree);
|
||||
|
||||
int dimension();
|
||||
uint32_t getPixelAt(unsigned int x, unsigned int y);
|
||||
uint8_t getAlphaAt(int x, int y);
|
||||
private:
|
||||
SquarePixelMapData* _data;
|
||||
PixelQuadTreeNode* _rootPixelQuadTreeNode;
|
||||
|
||||
void generateRootPixelQuadTreeNode();
|
||||
void createVoxelsFromPixelQuadTreeToVoxelTree(PixelQuadTreeNode* pixelQuadTreeNode, VoxelTree* voxelTree);
|
||||
VoxelDetail getVoxelDetail(PixelQuadTreeNode* pixelQuadTreeNode);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__SquarePixelMap__) */
|
|
@ -19,13 +19,13 @@
|
|||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QImage>
|
||||
#include <QRgb>
|
||||
|
||||
#include "CoverageMap.h"
|
||||
#include "GeometryUtil.h"
|
||||
#include "OctalCode.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "SharedUtil.h"
|
||||
#include "SquarePixelMap.h"
|
||||
#include "Tags.h"
|
||||
#include "ViewFrustum.h"
|
||||
#include "VoxelConstants.h"
|
||||
|
@ -52,6 +52,10 @@ VoxelTree::VoxelTree(bool shouldReaverage) :
|
|||
_shouldReaverage(shouldReaverage),
|
||||
_stopImport(false) {
|
||||
rootNode = new VoxelNode();
|
||||
|
||||
pthread_mutex_init(&_encodeSetLock, NULL);
|
||||
pthread_mutex_init(&_deleteSetLock, NULL);
|
||||
pthread_mutex_init(&_deletePendingSetLock, NULL);
|
||||
}
|
||||
|
||||
VoxelTree::~VoxelTree() {
|
||||
|
@ -60,6 +64,10 @@ VoxelTree::~VoxelTree() {
|
|||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||
delete rootNode->getChildAtIndex(i);
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&_encodeSetLock);
|
||||
pthread_mutex_destroy(&_deleteSetLock);
|
||||
pthread_mutex_destroy(&_deletePendingSetLock);
|
||||
}
|
||||
|
||||
|
||||
|
@ -397,7 +405,16 @@ void VoxelTree::deleteVoxelCodeFromTree(unsigned char* codeBuffer, bool collapse
|
|||
args.pathChanged = false;
|
||||
|
||||
VoxelNode* node = rootNode;
|
||||
deleteVoxelCodeFromTreeRecursion(node, &args);
|
||||
|
||||
// We can't encode and delete nodes at the same time, so we guard against deleting any node that is actively
|
||||
// being encoded. And we stick that code on our pendingDelete list.
|
||||
if (isEncoding(codeBuffer)) {
|
||||
queueForLaterDelete(codeBuffer);
|
||||
} else {
|
||||
startDeleting(codeBuffer);
|
||||
deleteVoxelCodeFromTreeRecursion(node, &args);
|
||||
doneDeleting(codeBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraData) {
|
||||
|
@ -1002,15 +1019,16 @@ bool VoxelTree::findCapsulePenetration(const glm::vec3& start, const glm::vec3&
|
|||
return args.found;
|
||||
}
|
||||
|
||||
|
||||
int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||
EncodeBitstreamParams& params) const {
|
||||
EncodeBitstreamParams& params) {
|
||||
|
||||
startEncoding(node);
|
||||
// How many bytes have we written so far at this level;
|
||||
int bytesWritten = 0;
|
||||
|
||||
// If we're at a node that is out of view, then we can return, because no nodes below us will be in view!
|
||||
if (params.viewFrustum && !node->isInView(*params.viewFrustum)) {
|
||||
doneEncoding(node);
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
|
@ -1061,6 +1079,8 @@ int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer,
|
|||
// otherwise... if we didn't write any child bytes, then pretend like we also didn't write our octal code
|
||||
bytesWritten = 0;
|
||||
}
|
||||
|
||||
doneEncoding(node);
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
|
@ -1587,25 +1607,61 @@ bool VoxelTree::readFromSVOFile(const char* fileName) {
|
|||
}
|
||||
|
||||
bool VoxelTree::readFromSquareARGB32Pixels(const char* filename) {
|
||||
QImage pngImage = QImage(filename);
|
||||
if (pngImage.height() != pngImage.width()) {
|
||||
qDebug("ERROR: Bad PNG size: height != width.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
emit importSize(1.0f, 1.0f, 1.0f);
|
||||
emit importProgress(0);
|
||||
|
||||
const uint32_t* pixels;
|
||||
if (pngImage.format() == QImage::Format_ARGB32) {
|
||||
pixels = reinterpret_cast<const uint32_t*>(pngImage.constBits());
|
||||
} else {
|
||||
QImage tmp = pngImage.convertToFormat(QImage::Format_ARGB32);
|
||||
pixels = reinterpret_cast<const uint32_t*>(tmp.constBits());
|
||||
int minAlpha = INT_MAX;
|
||||
|
||||
QImage pngImage = QImage(filename);
|
||||
|
||||
for (int x = 0; x < pngImage.width() * pngImage.height(); ++x) {
|
||||
minAlpha = std::min(qAlpha(pngImage.color(x)) , minAlpha);
|
||||
}
|
||||
|
||||
SquarePixelMap pixelMap = SquarePixelMap(pixels, pngImage.height());
|
||||
pixelMap.addVoxelsToVoxelTree(this);
|
||||
int maxSize = std::max(pngImage.width(), pngImage.height());
|
||||
|
||||
int scale = 1;
|
||||
while (maxSize > scale) {scale *= 2;}
|
||||
float size = 1.0f / scale;
|
||||
|
||||
QRgb pixel;
|
||||
int minNeighborhoodAlpha;
|
||||
|
||||
for (int i = 0; i < pngImage.width(); ++i) {
|
||||
for (int j = 0; j < pngImage.height(); ++j) {
|
||||
emit importProgress((100 * (i * pngImage.height() + j)) /
|
||||
(pngImage.width() * pngImage.height()));
|
||||
|
||||
pixel = pngImage.pixel(i, j);
|
||||
minNeighborhoodAlpha = qAlpha(pixel) - 1;
|
||||
|
||||
if (i != 0) {
|
||||
minNeighborhoodAlpha = std::min(minNeighborhoodAlpha, qAlpha(pngImage.pixel(i - 1, j)));
|
||||
}
|
||||
if (j != 0) {
|
||||
minNeighborhoodAlpha = std::min(minNeighborhoodAlpha, qAlpha(pngImage.pixel(i, j - 1)));
|
||||
}
|
||||
if (i < pngImage.width() - 1) {
|
||||
minNeighborhoodAlpha = std::min(minNeighborhoodAlpha, qAlpha(pngImage.pixel(i + 1, j)));
|
||||
}
|
||||
if (j < pngImage.height() - 1) {
|
||||
minNeighborhoodAlpha = std::min(minNeighborhoodAlpha, qAlpha(pngImage.pixel(i, j + 1)));
|
||||
}
|
||||
|
||||
while (qAlpha(pixel) > minNeighborhoodAlpha) {
|
||||
++minNeighborhoodAlpha;
|
||||
createVoxel(i * size,
|
||||
(minNeighborhoodAlpha - minAlpha) * size,
|
||||
j * size,
|
||||
size,
|
||||
qRed(pixel),
|
||||
qGreen(pixel),
|
||||
qBlue(pixel),
|
||||
true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
emit importProgress(100);
|
||||
return true;
|
||||
|
@ -1707,7 +1763,7 @@ bool VoxelTree::readFromSchematicFile(const char *fileName) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) const {
|
||||
void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) {
|
||||
|
||||
std::ofstream file(fileName, std::ios::out|std::ios::binary);
|
||||
|
||||
|
@ -1793,6 +1849,65 @@ void VoxelTree::copyFromTreeIntoSubTree(VoxelTree* sourceTree, VoxelNode* destin
|
|||
}
|
||||
}
|
||||
|
||||
void dumpSetContents(const char* name, std::set<unsigned char*> set) {
|
||||
printf("set %s has %ld elements\n", name, set.size());
|
||||
/*
|
||||
for (std::set<unsigned char*>::iterator i = set.begin(); i != set.end(); ++i) {
|
||||
printOctalCode(*i);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void VoxelTree::startEncoding(VoxelNode* node) {
|
||||
pthread_mutex_lock(&_encodeSetLock);
|
||||
_codesBeingEncoded.insert(node->getOctalCode());
|
||||
pthread_mutex_unlock(&_encodeSetLock);
|
||||
}
|
||||
|
||||
void VoxelTree::doneEncoding(VoxelNode* node) {
|
||||
pthread_mutex_lock(&_encodeSetLock);
|
||||
_codesBeingEncoded.erase(node->getOctalCode());
|
||||
pthread_mutex_unlock(&_encodeSetLock);
|
||||
|
||||
// if we have any pending delete codes, then delete them now.
|
||||
emptyDeleteQueue();
|
||||
}
|
||||
|
||||
void VoxelTree::startDeleting(unsigned char* code) {
|
||||
pthread_mutex_lock(&_deleteSetLock);
|
||||
_codesBeingDeleted.insert(code);
|
||||
pthread_mutex_unlock(&_deleteSetLock);
|
||||
}
|
||||
|
||||
void VoxelTree::doneDeleting(unsigned char* code) {
|
||||
pthread_mutex_lock(&_deleteSetLock);
|
||||
_codesBeingDeleted.erase(code);
|
||||
pthread_mutex_unlock(&_deleteSetLock);
|
||||
}
|
||||
|
||||
bool VoxelTree::isEncoding(unsigned char* codeBuffer) {
|
||||
pthread_mutex_lock(&_encodeSetLock);
|
||||
bool isEncoding = (_codesBeingEncoded.find(codeBuffer) != _codesBeingEncoded.end());
|
||||
pthread_mutex_unlock(&_encodeSetLock);
|
||||
return isEncoding;
|
||||
}
|
||||
|
||||
void VoxelTree::queueForLaterDelete(unsigned char* codeBuffer) {
|
||||
pthread_mutex_lock(&_deletePendingSetLock);
|
||||
_codesPendingDelete.insert(codeBuffer);
|
||||
pthread_mutex_unlock(&_deletePendingSetLock);
|
||||
}
|
||||
|
||||
void VoxelTree::emptyDeleteQueue() {
|
||||
pthread_mutex_lock(&_deletePendingSetLock);
|
||||
for (std::set<unsigned char*>::iterator i = _codesPendingDelete.begin(); i != _codesPendingDelete.end(); ++i) {
|
||||
unsigned char* codeToDelete = *i;
|
||||
_codesBeingDeleted.erase(codeToDelete);
|
||||
deleteVoxelCodeFromTree(codeToDelete, COLLAPSE_EMPTY_TREE);
|
||||
}
|
||||
pthread_mutex_unlock(&_deletePendingSetLock);
|
||||
}
|
||||
|
||||
void VoxelTree::cancelImport() {
|
||||
_stopImport = true;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#ifndef __hifi__VoxelTree__
|
||||
#define __hifi__VoxelTree__
|
||||
|
||||
#include <set>
|
||||
#include <PointerStack.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
|
||||
|
@ -155,7 +156,7 @@ public:
|
|||
const glm::vec3& point, void* extraData=NULL);
|
||||
|
||||
int encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, int availableBytes, VoxelNodeBag& bag,
|
||||
EncodeBitstreamParams& params) const;
|
||||
EncodeBitstreamParams& params) ;
|
||||
|
||||
bool isDirty() const { return _isDirty; };
|
||||
void clearDirtyBit() { _isDirty = false; };
|
||||
|
@ -172,7 +173,7 @@ public:
|
|||
void loadVoxelsFile(const char* fileName, bool wantColorRandomizer);
|
||||
|
||||
// these will read/write files that match the wireformat, excluding the 'V' leading
|
||||
void writeToSVOFile(const char* filename, VoxelNode* node = NULL) const;
|
||||
void writeToSVOFile(const char* filename, VoxelNode* node = NULL);
|
||||
bool readFromSVOFile(const char* filename);
|
||||
// reads voxels from square image with alpha as a Y-axis
|
||||
bool readFromSquareARGB32Pixels(const char *filename);
|
||||
|
@ -219,6 +220,40 @@ private:
|
|||
unsigned long int _nodesChangedFromBitstream;
|
||||
bool _shouldReaverage;
|
||||
bool _stopImport;
|
||||
|
||||
/// Octal Codes of any subtrees currently being encoded. While any of these codes is being encoded, ancestors and
|
||||
/// descendants of them can not be deleted.
|
||||
std::set<unsigned char*> _codesBeingEncoded;
|
||||
/// mutex lock to protect the encoding set
|
||||
pthread_mutex_t _encodeSetLock;
|
||||
|
||||
/// Called to indicate that a VoxelNode is in the process of being encoded.
|
||||
void startEncoding(VoxelNode* node);
|
||||
/// Called to indicate that a VoxelNode is done being encoded.
|
||||
void doneEncoding(VoxelNode* node);
|
||||
/// Is the Octal Code currently being deleted?
|
||||
bool isEncoding(unsigned char* codeBuffer);
|
||||
|
||||
/// Octal Codes of any subtrees currently being deleted. While any of these codes is being deleted, ancestors and
|
||||
/// descendants of them can not be encoded.
|
||||
std::set<unsigned char*> _codesBeingDeleted;
|
||||
/// mutex lock to protect the deleting set
|
||||
pthread_mutex_t _deleteSetLock;
|
||||
|
||||
/// Called to indicate that an octal code is in the process of being deleted.
|
||||
void startDeleting(unsigned char* code);
|
||||
/// Called to indicate that an octal code is done being deleted.
|
||||
void doneDeleting(unsigned char* code);
|
||||
/// Octal Codes that were attempted to be deleted but couldn't be because they were actively being encoded, and were
|
||||
/// instead queued for later delete
|
||||
std::set<unsigned char*> _codesPendingDelete;
|
||||
/// mutex lock to protect the deleting set
|
||||
pthread_mutex_t _deletePendingSetLock;
|
||||
|
||||
/// Adds an Octal Code to the set of codes that needs to be deleted
|
||||
void queueForLaterDelete(unsigned char* codeBuffer);
|
||||
/// flushes out any Octal Codes that had to be queued
|
||||
void emptyDeleteQueue();
|
||||
};
|
||||
|
||||
float boundaryDistanceForRenderLevel(unsigned int renderLevel);
|
||||
|
|
37
voxel-server/src/VoxelPersistThread.cpp
Normal file
37
voxel-server/src/VoxelPersistThread.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// VoxelPersistThread.cpp
|
||||
// voxel-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded voxel persistance
|
||||
//
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "VoxelPersistThread.h"
|
||||
#include "VoxelServer.h"
|
||||
|
||||
VoxelPersistThread::VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval) :
|
||||
_tree(tree),
|
||||
_filename(filename),
|
||||
_persistInterval(persistInterval) {
|
||||
}
|
||||
|
||||
bool VoxelPersistThread::process() {
|
||||
uint64_t MSECS_TO_USECS = 1000;
|
||||
usleep(_persistInterval * MSECS_TO_USECS);
|
||||
|
||||
|
||||
// check the dirty bit and persist here...
|
||||
if (_tree->isDirty()) {
|
||||
printf("saving voxels to file %s...\n",_filename);
|
||||
_tree->writeToSVOFile(_filename);
|
||||
_tree->clearDirtyBit(); // tree is clean after saving
|
||||
printf("DONE saving voxels to file...\n");
|
||||
}
|
||||
|
||||
return isStillRunning(); // keep running till they terminate us
|
||||
}
|
33
voxel-server/src/VoxelPersistThread.h
Normal file
33
voxel-server/src/VoxelPersistThread.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// VoxelPersistThread.h
|
||||
// voxel-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded voxel persistance
|
||||
//
|
||||
|
||||
#ifndef __voxel_server__VoxelPersistThread__
|
||||
#define __voxel_server__VoxelPersistThread__
|
||||
|
||||
#include <GenericThread.h>
|
||||
#include <NetworkPacket.h>
|
||||
#include <VoxelTree.h>
|
||||
|
||||
/// Generalized threaded processor for handling received inbound packets.
|
||||
class VoxelPersistThread : public virtual GenericThread {
|
||||
public:
|
||||
static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
|
||||
|
||||
VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
|
||||
protected:
|
||||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process();
|
||||
private:
|
||||
VoxelTree* _tree;
|
||||
const char* _filename;
|
||||
int _persistInterval;
|
||||
};
|
||||
|
||||
#endif // __voxel_server__VoxelPersistThread__
|
58
voxel-server/src/VoxelServer.h
Normal file
58
voxel-server/src/VoxelServer.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
// VoxelServer.h
|
||||
// voxel-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __voxel_server__VoxelServer__
|
||||
#define __voxel_server__VoxelServer__
|
||||
|
||||
#include <SharedUtil.h>
|
||||
#include <NodeList.h> // for MAX_PACKET_SIZE
|
||||
#include <EnvironmentData.h>
|
||||
#include <JurisdictionSender.h>
|
||||
#include <VoxelTree.h>
|
||||
|
||||
#include "VoxelServerPacketProcessor.h"
|
||||
|
||||
|
||||
const int MAX_FILENAME_LENGTH = 1024;
|
||||
const int VOXEL_LISTEN_PORT = 40106;
|
||||
const int VOXEL_SIZE_BYTES = 3 + (3 * sizeof(float));
|
||||
const int VOXELS_PER_PACKET = (MAX_PACKET_SIZE - 1) / VOXEL_SIZE_BYTES;
|
||||
const int MIN_BRIGHTNESS = 64;
|
||||
const float DEATH_STAR_RADIUS = 4.0;
|
||||
const float MAX_CUBE = 0.05f;
|
||||
const int VOXEL_SEND_INTERVAL_USECS = 17 * 1000; // approximately 60fps
|
||||
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels
|
||||
const int INTERVALS_PER_SECOND = 1000 * 1000 / VOXEL_SEND_INTERVAL_USECS;
|
||||
const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4;
|
||||
const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000;
|
||||
|
||||
extern const char* LOCAL_VOXELS_PERSIST_FILE;
|
||||
extern const char* VOXELS_PERSIST_FILE;
|
||||
extern char voxelPersistFilename[MAX_FILENAME_LENGTH];
|
||||
extern int PACKETS_PER_CLIENT_PER_INTERVAL;
|
||||
|
||||
extern VoxelTree serverTree; // this IS a reaveraging tree
|
||||
extern bool wantVoxelPersist;
|
||||
extern bool wantLocalDomain;
|
||||
extern bool debugVoxelSending;
|
||||
extern bool shouldShowAnimationDebug;
|
||||
extern bool displayVoxelStats;
|
||||
extern bool debugVoxelReceiving;
|
||||
extern bool sendEnvironments;
|
||||
extern bool sendMinimalEnvironment;
|
||||
extern bool dumpVoxelsOnMove;
|
||||
extern EnvironmentData environmentData[3];
|
||||
extern int receivedPacketCount;
|
||||
extern JurisdictionMap* jurisdiction;
|
||||
extern JurisdictionSender* jurisdictionSender;
|
||||
extern VoxelServerPacketProcessor* voxelServerPacketProcessor;
|
||||
extern pthread_mutex_t treeLock;
|
||||
|
||||
|
||||
|
||||
#endif // __voxel_server__VoxelServer__
|
117
voxel-server/src/VoxelServerPacketProcessor.cpp
Normal file
117
voxel-server/src/VoxelServerPacketProcessor.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// VoxelServerPacketProcessor.cpp
|
||||
// voxel-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded network packet processor for the voxel-server
|
||||
//
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "VoxelServer.h"
|
||||
#include "VoxelServerPacketProcessor.h"
|
||||
|
||||
|
||||
void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) {
|
||||
bool destructive = (packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
|
||||
PerformanceWarning warn(::shouldShowAnimationDebug,
|
||||
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
|
||||
::shouldShowAnimationDebug);
|
||||
|
||||
::receivedPacketCount++;
|
||||
|
||||
unsigned short int itemNumber = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
|
||||
if (::shouldShowAnimationDebug) {
|
||||
printf("got %s - command from client receivedBytes=%ld itemNumber=%d\n",
|
||||
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
|
||||
packetLength, itemNumber);
|
||||
}
|
||||
|
||||
if (::debugVoxelReceiving) {
|
||||
printf("got %s - %d command from client receivedBytes=%ld itemNumber=%d\n",
|
||||
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
|
||||
::receivedPacketCount, packetLength, itemNumber);
|
||||
}
|
||||
int atByte = numBytesPacketHeader + sizeof(itemNumber);
|
||||
unsigned char* voxelData = (unsigned char*)&packetData[atByte];
|
||||
while (atByte < packetLength) {
|
||||
unsigned char octets = (unsigned char)*voxelData;
|
||||
const int COLOR_SIZE_IN_BYTES = 3;
|
||||
int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES;
|
||||
int voxelCodeSize = bytesRequiredForCodeLength(octets);
|
||||
|
||||
if (::shouldShowAnimationDebug) {
|
||||
int red = voxelData[voxelCodeSize + 0];
|
||||
int green = voxelData[voxelCodeSize + 1];
|
||||
int blue = voxelData[voxelCodeSize + 2];
|
||||
|
||||
float* vertices = firstVertexForCode(voxelData);
|
||||
printf("inserting voxel: %f,%f,%f r=%d,g=%d,b=%d\n", vertices[0], vertices[1], vertices[2], red, green, blue);
|
||||
delete[] vertices;
|
||||
}
|
||||
|
||||
serverTree.readCodeColorBufferToTree(voxelData, destructive);
|
||||
// skip to next
|
||||
voxelData += voxelDataSize;
|
||||
atByte += voxelDataSize;
|
||||
}
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
if (node) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
|
||||
} else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) {
|
||||
|
||||
// Send these bits off to the VoxelTree class to process them
|
||||
pthread_mutex_lock(&::treeLock);
|
||||
::serverTree.processRemoveVoxelBitstream((unsigned char*)packetData, packetLength);
|
||||
pthread_mutex_unlock(&::treeLock);
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
if (node) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
} else if (packetData[0] == PACKET_TYPE_Z_COMMAND) {
|
||||
|
||||
// the Z command is a special command that allows the sender to send the voxel server high level semantic
|
||||
// requests, like erase all, or add sphere scene
|
||||
|
||||
char* command = (char*) &packetData[numBytesPacketHeader]; // start of the command
|
||||
int commandLength = strlen(command); // commands are null terminated strings
|
||||
int totalLength = numBytesPacketHeader + commandLength + 1; // 1 for null termination
|
||||
printf("got Z message len(%ld)= %s\n", packetLength, command);
|
||||
bool rebroadcast = true; // by default rebroadcast
|
||||
|
||||
while (totalLength <= packetLength) {
|
||||
if (strcmp(command, TEST_COMMAND) == 0) {
|
||||
printf("got Z message == a message, nothing to do, just report\n");
|
||||
}
|
||||
totalLength += commandLength + 1; // 1 for null termination
|
||||
}
|
||||
|
||||
if (rebroadcast) {
|
||||
// Now send this to the connected nodes so they can also process these messages
|
||||
printf("rebroadcasting Z message to connected nodes... nodeList.broadcastToNodes()\n");
|
||||
NodeList::getInstance()->broadcastToNodes(packetData, packetLength, &NODE_TYPE_AGENT, 1);
|
||||
}
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
|
||||
if (node) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
} else {
|
||||
printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]);
|
||||
}
|
||||
}
|
||||
|
22
voxel-server/src/VoxelServerPacketProcessor.h
Normal file
22
voxel-server/src/VoxelServerPacketProcessor.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// VoxelServerPacketProcessor.h
|
||||
// voxel-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded network packet processor for the voxel-server
|
||||
//
|
||||
|
||||
#ifndef __voxel_server__VoxelServerPacketProcessor__
|
||||
#define __voxel_server__VoxelServerPacketProcessor__
|
||||
|
||||
#include <ReceivedPacketProcessor.h>
|
||||
|
||||
/// Handles processing of incoming network packets for the voxel-server. As with other ReceivedPacketProcessor classes
|
||||
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
|
||||
class VoxelServerPacketProcessor : public ReceivedPacketProcessor {
|
||||
protected:
|
||||
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
|
||||
};
|
||||
#endif // __voxel_server__VoxelServerPacketProcessor__
|
|
@ -10,6 +10,7 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <OctalCode.h>
|
||||
#include <NodeList.h>
|
||||
#include <NodeTypes.h>
|
||||
|
@ -20,9 +21,11 @@
|
|||
#include <PacketHeaders.h>
|
||||
#include <SceneUtils.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include <JurisdictionSender.h>
|
||||
|
||||
#include "VoxelPersistThread.h"
|
||||
#include "VoxelServerPacketProcessor.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Syssocket.h"
|
||||
#include "Systime.h"
|
||||
|
@ -32,37 +35,15 @@
|
|||
#include <ifaddrs.h>
|
||||
#endif
|
||||
|
||||
#include "VoxelServer.h"
|
||||
|
||||
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
|
||||
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo";
|
||||
const int MAX_FILENAME_LENGTH = 1024;
|
||||
char voxelPersistFilename[MAX_FILENAME_LENGTH];
|
||||
const int VOXEL_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
|
||||
|
||||
const int VOXEL_LISTEN_PORT = 40106;
|
||||
|
||||
|
||||
const int VOXEL_SIZE_BYTES = 3 + (3 * sizeof(float));
|
||||
const int VOXELS_PER_PACKET = (MAX_PACKET_SIZE - 1) / VOXEL_SIZE_BYTES;
|
||||
|
||||
const int MIN_BRIGHTNESS = 64;
|
||||
const float DEATH_STAR_RADIUS = 4.0;
|
||||
const float MAX_CUBE = 0.05f;
|
||||
|
||||
const int VOXEL_SEND_INTERVAL_USECS = 17 * 1000; // approximately 60fps
|
||||
int PACKETS_PER_CLIENT_PER_INTERVAL = 10;
|
||||
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels
|
||||
const int INTERVALS_PER_SECOND = 1000 * 1000 / VOXEL_SEND_INTERVAL_USECS;
|
||||
|
||||
const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4;
|
||||
|
||||
const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000;
|
||||
|
||||
VoxelTree serverTree(true); // this IS a reaveraging tree
|
||||
bool wantVoxelPersist = true;
|
||||
bool wantLocalDomain = false;
|
||||
|
||||
|
||||
bool wantColorRandomizer = false;
|
||||
bool debugVoxelSending = false;
|
||||
bool shouldShowAnimationDebug = false;
|
||||
bool displayVoxelStats = false;
|
||||
|
@ -70,60 +51,15 @@ bool debugVoxelReceiving = false;
|
|||
bool sendEnvironments = true;
|
||||
bool sendMinimalEnvironment = false;
|
||||
bool dumpVoxelsOnMove = false;
|
||||
|
||||
EnvironmentData environmentData[3];
|
||||
|
||||
int receivedPacketCount = 0;
|
||||
JurisdictionMap* jurisdiction = NULL;
|
||||
JurisdictionSender* jurisdictionSender = NULL;
|
||||
|
||||
void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) {
|
||||
// randomly generate children for this node
|
||||
// the first level of the tree (where levelsToGo = MAX_VOXEL_TREE_DEPTH_LEVELS) has all 8
|
||||
if (levelsToGo > 0) {
|
||||
|
||||
bool createdChildren = false;
|
||||
createdChildren = false;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (true) {
|
||||
// create a new VoxelNode to put here
|
||||
currentRootNode->addChildAtIndex(i);
|
||||
randomlyFillVoxelTree(levelsToGo - 1, currentRootNode->getChildAtIndex(i));
|
||||
createdChildren = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!createdChildren) {
|
||||
// we didn't create any children for this node, making it a leaf
|
||||
// give it a random color
|
||||
currentRootNode->setRandomColor(MIN_BRIGHTNESS);
|
||||
} else {
|
||||
// set the color value for this node
|
||||
currentRootNode->setColorFromAverageOfChildren();
|
||||
}
|
||||
} else {
|
||||
// this is a leaf node, just give it a color
|
||||
currentRootNode->setRandomColor(MIN_BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
|
||||
void eraseVoxelTreeAndCleanupNodeVisitData() {
|
||||
|
||||
// As our tree to erase all it's voxels
|
||||
::serverTree.eraseAllVoxels();
|
||||
// enumerate the nodes clean up their marker nodes
|
||||
for (NodeList::iterator node = NodeList::getInstance()->begin(); node != NodeList::getInstance()->end(); node++) {
|
||||
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
|
||||
if (nodeData) {
|
||||
// clean up the node visit data
|
||||
nodeData->nodeBag.deleteAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VoxelServerPacketProcessor* voxelServerPacketProcessor = NULL;
|
||||
VoxelPersistThread* voxelPersistThread = NULL;
|
||||
pthread_mutex_t treeLock;
|
||||
|
||||
|
||||
void handlePacketSend(NodeList* nodeList,
|
||||
NodeList::iterator& node,
|
||||
VoxelNodeData* nodeData,
|
||||
|
@ -161,14 +97,12 @@ void handlePacketSend(NodeList* nodeList,
|
|||
nodeData->resetVoxelPacket();
|
||||
}
|
||||
|
||||
|
||||
// Version of voxel distributor that sends the deepest LOD level at once
|
||||
void deepestLevelVoxelDistributor(NodeList* nodeList,
|
||||
NodeList::iterator& node,
|
||||
VoxelNodeData* nodeData,
|
||||
bool viewFrustumChanged) {
|
||||
|
||||
|
||||
pthread_mutex_lock(&::treeLock);
|
||||
|
||||
int truePacketsSent = 0;
|
||||
|
@ -380,29 +314,6 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
|
|||
pthread_mutex_unlock(&::treeLock);
|
||||
}
|
||||
|
||||
uint64_t lastPersistVoxels = 0;
|
||||
void persistVoxelsWhenDirty() {
|
||||
uint64_t now = usecTimestampNow();
|
||||
if (::lastPersistVoxels == 0) {
|
||||
::lastPersistVoxels = now;
|
||||
}
|
||||
int sinceLastTime = (now - ::lastPersistVoxels) / 1000;
|
||||
|
||||
// check the dirty bit and persist here...
|
||||
if (::wantVoxelPersist && ::serverTree.isDirty() && sinceLastTime > VOXEL_PERSIST_INTERVAL) {
|
||||
{
|
||||
PerformanceWarning warn(::shouldShowAnimationDebug,
|
||||
"persistVoxelsWhenDirty() - writeToSVOFile()", ::shouldShowAnimationDebug);
|
||||
|
||||
printf("saving voxels to file...\n");
|
||||
serverTree.writeToSVOFile(::voxelPersistFilename);
|
||||
serverTree.clearDirtyBit(); // tree is clean after saving
|
||||
printf("DONE saving voxels to file...\n");
|
||||
}
|
||||
::lastPersistVoxels = usecTimestampNow();
|
||||
}
|
||||
}
|
||||
|
||||
void* distributeVoxelsToListeners(void* args) {
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
@ -510,9 +421,6 @@ int main(int argc, const char * argv[]) {
|
|||
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, listenPort);
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
const NODE_TYPE nodeTypes[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER };
|
||||
NodeList::getInstance()->setNodeTypesOfInterest(&nodeTypes[0], sizeof(nodeTypes));
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* local = "--local";
|
||||
::wantLocalDomain = cmdOptionExists(argc, argv,local);
|
||||
|
@ -547,10 +455,6 @@ int main(int argc, const char * argv[]) {
|
|||
::shouldShowAnimationDebug = cmdOptionExists(argc, argv, WANT_ANIMATION_DEBUG);
|
||||
printf("shouldShowAnimationDebug=%s\n", debug::valueOf(::shouldShowAnimationDebug));
|
||||
|
||||
const char* WANT_COLOR_RANDOMIZER = "--wantColorRandomizer";
|
||||
::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER);
|
||||
printf("wantColorRandomizer=%s\n", debug::valueOf(::wantColorRandomizer));
|
||||
|
||||
// By default we will voxel persist, if you want to disable this, then pass in this parameter
|
||||
const char* NO_VOXEL_PERSIST = "--NoVoxelPersist";
|
||||
if (cmdOptionExists(argc, argv, NO_VOXEL_PERSIST)) {
|
||||
|
@ -589,6 +493,12 @@ int main(int argc, const char * argv[]) {
|
|||
unsigned long internalNodeCount = ::serverTree.rootNode->getSubTreeInternalNodeCount();
|
||||
unsigned long leafNodeCount = ::serverTree.rootNode->getSubTreeLeafNodeCount();
|
||||
printf("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount);
|
||||
|
||||
// now set up VoxelPersistThread
|
||||
::voxelPersistThread = new VoxelPersistThread(&::serverTree, ::voxelPersistFilename);
|
||||
if (::voxelPersistThread) {
|
||||
::voxelPersistThread->initialize(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if the user passed in a command line option for loading an old style local
|
||||
|
@ -610,32 +520,6 @@ int main(int argc, const char * argv[]) {
|
|||
printf("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, PACKETS_PER_CLIENT_PER_INTERVAL);
|
||||
}
|
||||
|
||||
const char* ADD_RANDOM_VOXELS = "--AddRandomVoxels";
|
||||
if (cmdOptionExists(argc, argv, ADD_RANDOM_VOXELS)) {
|
||||
// create an octal code buffer and load it with 0 so that the recursive tree fill can give
|
||||
// octal codes to the tree nodes that it is creating
|
||||
randomlyFillVoxelTree(MAX_VOXEL_TREE_DEPTH_LEVELS, serverTree.rootNode);
|
||||
}
|
||||
|
||||
const char* ADD_SCENE = "--AddScene";
|
||||
bool addScene = cmdOptionExists(argc, argv, ADD_SCENE);
|
||||
const char* NO_ADD_SCENE = "--NoAddScene";
|
||||
bool noAddScene = cmdOptionExists(argc, argv, NO_ADD_SCENE);
|
||||
if (addScene && noAddScene) {
|
||||
printf("WARNING! --AddScene and --NoAddScene are mutually exclusive. We will honor --NoAddScene\n");
|
||||
}
|
||||
|
||||
// We will add a scene if...
|
||||
// 1) we attempted to load a persistant file and it wasn't there
|
||||
// 2) you asked us to add a scene
|
||||
// HOWEVER -- we will NEVER add a scene if you explicitly tell us not to!
|
||||
//
|
||||
// TEMPORARILY DISABLED!!!
|
||||
bool actuallyAddScene = false; // !noAddScene && (addScene || (::wantVoxelPersist && !persistantFileRead));
|
||||
if (actuallyAddScene) {
|
||||
addSphereScene(&serverTree);
|
||||
}
|
||||
|
||||
// for now, initialize the environments with fixed values
|
||||
environmentData[1].setID(1);
|
||||
environmentData[1].setGravity(1.0f);
|
||||
|
@ -652,10 +536,10 @@ int main(int argc, const char * argv[]) {
|
|||
pthread_t sendVoxelThread;
|
||||
pthread_create(&sendVoxelThread, NULL, distributeVoxelsToListeners, NULL);
|
||||
|
||||
sockaddr nodePublicAddress;
|
||||
sockaddr senderAddress;
|
||||
|
||||
unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE];
|
||||
ssize_t receivedBytes;
|
||||
ssize_t packetLength;
|
||||
|
||||
timeval lastDomainServerCheckIn = {};
|
||||
|
||||
|
@ -664,6 +548,12 @@ int main(int argc, const char * argv[]) {
|
|||
if (::jurisdictionSender) {
|
||||
::jurisdictionSender->initialize(true);
|
||||
}
|
||||
|
||||
// set up our VoxelServerPacketProcessor
|
||||
::voxelServerPacketProcessor = new VoxelServerPacketProcessor();
|
||||
if (::voxelServerPacketProcessor) {
|
||||
::voxelServerPacketProcessor->initialize(true);
|
||||
}
|
||||
|
||||
// loop to send to nodes requesting data
|
||||
while (true) {
|
||||
|
@ -674,155 +564,33 @@ int main(int argc, const char * argv[]) {
|
|||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
}
|
||||
|
||||
// check to see if we need to persist our voxel state
|
||||
persistVoxelsWhenDirty();
|
||||
|
||||
if (nodeList->getNodeSocket()->receive(&nodePublicAddress, packetData, &receivedBytes) &&
|
||||
if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) &&
|
||||
packetVersionMatch(packetData)) {
|
||||
|
||||
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
|
||||
if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) {
|
||||
bool destructive = (packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
|
||||
PerformanceWarning warn(::shouldShowAnimationDebug,
|
||||
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
|
||||
::shouldShowAnimationDebug);
|
||||
|
||||
::receivedPacketCount++;
|
||||
|
||||
unsigned short int itemNumber = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
|
||||
if (::shouldShowAnimationDebug) {
|
||||
printf("got %s - command from client receivedBytes=%ld itemNumber=%d\n",
|
||||
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
|
||||
receivedBytes,itemNumber);
|
||||
}
|
||||
|
||||
if (::debugVoxelReceiving) {
|
||||
printf("got %s - %d command from client receivedBytes=%ld itemNumber=%d\n",
|
||||
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
|
||||
::receivedPacketCount, receivedBytes,itemNumber);
|
||||
}
|
||||
int atByte = numBytesPacketHeader + sizeof(itemNumber);
|
||||
unsigned char* voxelData = (unsigned char*)&packetData[atByte];
|
||||
while (atByte < receivedBytes) {
|
||||
unsigned char octets = (unsigned char)*voxelData;
|
||||
const int COLOR_SIZE_IN_BYTES = 3;
|
||||
int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES;
|
||||
int voxelCodeSize = bytesRequiredForCodeLength(octets);
|
||||
|
||||
// color randomization on insert
|
||||
int colorRandomizer = ::wantColorRandomizer ? randIntInRange (-50, 50) : 0;
|
||||
int red = voxelData[voxelCodeSize + 0];
|
||||
int green = voxelData[voxelCodeSize + 1];
|
||||
int blue = voxelData[voxelCodeSize + 2];
|
||||
|
||||
if (::shouldShowAnimationDebug) {
|
||||
printf("insert voxels - wantColorRandomizer=%s old r=%d,g=%d,b=%d \n",
|
||||
(::wantColorRandomizer?"yes":"no"),red,green,blue);
|
||||
}
|
||||
|
||||
red = std::max(0, std::min(255, red + colorRandomizer));
|
||||
green = std::max(0, std::min(255, green + colorRandomizer));
|
||||
blue = std::max(0, std::min(255, blue + colorRandomizer));
|
||||
|
||||
if (::shouldShowAnimationDebug) {
|
||||
printf("insert voxels - wantColorRandomizer=%s NEW r=%d,g=%d,b=%d \n",
|
||||
(::wantColorRandomizer?"yes":"no"),red,green,blue);
|
||||
}
|
||||
voxelData[voxelCodeSize + 0] = red;
|
||||
voxelData[voxelCodeSize + 1] = green;
|
||||
voxelData[voxelCodeSize + 2] = blue;
|
||||
|
||||
if (::shouldShowAnimationDebug) {
|
||||
float* vertices = firstVertexForCode(voxelData);
|
||||
printf("inserting voxel at: %f,%f,%f\n", vertices[0], vertices[1], vertices[2]);
|
||||
delete []vertices;
|
||||
}
|
||||
|
||||
serverTree.readCodeColorBufferToTree(voxelData, destructive);
|
||||
// skip to next
|
||||
voxelData += voxelDataSize;
|
||||
atByte += voxelDataSize;
|
||||
}
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&nodePublicAddress);
|
||||
if (node) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
|
||||
} else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) {
|
||||
|
||||
// Send these bits off to the VoxelTree class to process them
|
||||
pthread_mutex_lock(&::treeLock);
|
||||
serverTree.processRemoveVoxelBitstream((unsigned char*)packetData, receivedBytes);
|
||||
pthread_mutex_unlock(&::treeLock);
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&nodePublicAddress);
|
||||
if (node) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
} else if (packetData[0] == PACKET_TYPE_Z_COMMAND) {
|
||||
|
||||
// the Z command is a special command that allows the sender to send the voxel server high level semantic
|
||||
// requests, like erase all, or add sphere scene
|
||||
|
||||
char* command = (char*) &packetData[numBytesPacketHeader]; // start of the command
|
||||
int commandLength = strlen(command); // commands are null terminated strings
|
||||
int totalLength = numBytesPacketHeader + commandLength + 1; // 1 for null termination
|
||||
printf("got Z message len(%ld)= %s\n", receivedBytes, command);
|
||||
bool rebroadcast = true; // by default rebroadcast
|
||||
|
||||
while (totalLength <= receivedBytes) {
|
||||
if (strcmp(command, ERASE_ALL_COMMAND) == 0) {
|
||||
printf("got Z message == erase all\n");
|
||||
eraseVoxelTreeAndCleanupNodeVisitData();
|
||||
rebroadcast = false;
|
||||
}
|
||||
if (strcmp(command, ADD_SCENE_COMMAND) == 0) {
|
||||
printf("got Z message == add scene\n");
|
||||
addSphereScene(&serverTree);
|
||||
rebroadcast = false;
|
||||
}
|
||||
if (strcmp(command, TEST_COMMAND) == 0) {
|
||||
printf("got Z message == a message, nothing to do, just report\n");
|
||||
}
|
||||
totalLength += commandLength + 1; // 1 for null termination
|
||||
}
|
||||
|
||||
if (rebroadcast) {
|
||||
// Now send this to the connected nodes so they can also process these messages
|
||||
printf("rebroadcasting Z message to connected nodes... nodeList.broadcastToNodes()\n");
|
||||
nodeList->broadcastToNodes(packetData, receivedBytes, &NODE_TYPE_AGENT, 1);
|
||||
}
|
||||
|
||||
// Make sure our Node and NodeList knows we've heard from this node.
|
||||
Node* node = NodeList::getInstance()->nodeWithAddress(&nodePublicAddress);
|
||||
if (node) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
} else if (packetData[0] == PACKET_TYPE_HEAD_DATA) {
|
||||
if (packetData[0] == PACKET_TYPE_HEAD_DATA) {
|
||||
// If we got a PACKET_TYPE_HEAD_DATA, then we're talking to an NODE_TYPE_AVATAR, and we
|
||||
// need to make sure we have it in our nodeList.
|
||||
|
||||
uint16_t nodeID = 0;
|
||||
unpackNodeId(packetData + numBytesPacketHeader, &nodeID);
|
||||
Node* node = nodeList->addOrUpdateNode(&nodePublicAddress,
|
||||
&nodePublicAddress,
|
||||
Node* node = NodeList::getInstance()->addOrUpdateNode(&senderAddress,
|
||||
&senderAddress,
|
||||
NODE_TYPE_AGENT,
|
||||
nodeID);
|
||||
|
||||
nodeList->updateNodeWithData(node, packetData, receivedBytes);
|
||||
|
||||
NodeList::getInstance()->updateNodeWithData(node, packetData, packetLength);
|
||||
} else if (packetData[0] == PACKET_TYPE_PING) {
|
||||
// If the packet is a ping, let processNodeData handle it.
|
||||
nodeList->processNodeData(&nodePublicAddress, packetData, receivedBytes);
|
||||
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
|
||||
} else if (packetData[0] == PACKET_TYPE_DOMAIN) {
|
||||
nodeList->processNodeData(&nodePublicAddress, packetData, receivedBytes);
|
||||
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
|
||||
} else if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
|
||||
if (::jurisdictionSender) {
|
||||
jurisdictionSender->queueReceivedPacket(nodePublicAddress, packetData, receivedBytes);
|
||||
::jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength);
|
||||
}
|
||||
} else if (::voxelServerPacketProcessor) {
|
||||
::voxelServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength);
|
||||
} else {
|
||||
printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]);
|
||||
}
|
||||
|
@ -837,10 +605,20 @@ int main(int argc, const char * argv[]) {
|
|||
}
|
||||
|
||||
if (::jurisdictionSender) {
|
||||
jurisdictionSender->terminate();
|
||||
::jurisdictionSender->terminate();
|
||||
delete ::jurisdictionSender;
|
||||
}
|
||||
|
||||
if (::voxelServerPacketProcessor) {
|
||||
::voxelServerPacketProcessor->terminate();
|
||||
delete ::voxelServerPacketProcessor;
|
||||
}
|
||||
|
||||
if (::voxelPersistThread) {
|
||||
::voxelPersistThread->terminate();
|
||||
delete ::voxelPersistThread;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue