Conflicts:
	interface/src/avatar/Avatar.cpp
This commit is contained in:
Andrzej Kapolka 2013-08-22 10:40:15 -07:00
commit ff245427fd
16 changed files with 1952 additions and 1796 deletions

View file

@ -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

View file

@ -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

View file

@ -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 {

File diff suppressed because it is too large Load diff

View 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

View file

@ -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;
}

View file

@ -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__) */

View file

@ -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;
}

View file

@ -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);

View 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
}

View 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__

View 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__

View 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]);
}
}

View 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__

View file

@ -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;
}