mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-08 06:32:35 +02:00
Merge branch 'master' of github.com:worklist/hifi into assignment
This commit is contained in:
commit
b64e3dd82f
33 changed files with 1232 additions and 700 deletions
111
README.md
111
README.md
|
@ -8,17 +8,18 @@ In this repository you'll find the source to many of the components in our
|
|||
alpha-stage virtual world. The project embraces distributed development
|
||||
and if you'd like to help, we'll pay you -- find out more at Worklist.net.
|
||||
If you find a small bug and have a fix, pull requests are welcome. If you'd
|
||||
like to get paid for your work, make sure you report the bug via a job on Worklist.net.
|
||||
like to get paid for your work, make sure you report the bug via a job on
|
||||
Worklist.net.
|
||||
|
||||
We're hiring! We're looking for skilled developers;
|
||||
send your resume to hiring@highfidelity.io
|
||||
|
||||
|
||||
Building Interface
|
||||
Building Interface & other High Fidelity Components
|
||||
=========
|
||||
|
||||
Interface is our OS X and Linux build-able
|
||||
client for accessing our virtual world.
|
||||
Interface is our OS X and Linux build-able client for accessing our virtual
|
||||
world.
|
||||
|
||||
CMake
|
||||
-----
|
||||
|
@ -27,7 +28,8 @@ for your platform. You can download CMake at cmake.org
|
|||
|
||||
Create a build directory in the root of your checkout and then run the
|
||||
CMake build from there. This will keep the rest of the directory clean,
|
||||
and makes the gitignore a little easier to handle (since we can just ignore build).
|
||||
and makes the gitignore a little easier to handle (since we can just ignore
|
||||
build).
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
@ -37,40 +39,105 @@ Those are the commands used on OS X to run CMake from the build folder
|
|||
and generate Xcode project files. If you are building on a *nix system,
|
||||
you'll run something like "cmake .." (this will depend on your exact needs)
|
||||
|
||||
Building in XCode
|
||||
-----
|
||||
|
||||
After running cmake, you will have the make files or Xcode project file
|
||||
necessary to build all of the components. For OS X, load Xcode, open the
|
||||
hifi.xcodeproj file, choose ALL_BUILD from the Product > Scheme menu (or target
|
||||
drop down), and click Run.
|
||||
|
||||
If the build completes successfully, you will have built targets for all HiFi
|
||||
components located in the build/target_name/Debug directories.
|
||||
|
||||
Other dependencies & information
|
||||
----
|
||||
In addition to CMake, Qt 5.1 is required to build all components.
|
||||
|
||||
What can I build on?
|
||||
We have successfully built on OS X 10.8, Ubuntu and a few other modern Linux distributions.
|
||||
A Windows build is planned for the future, but not currently in development.
|
||||
We have successfully built on OS X 10.8, Ubuntu and a few other modern Linux
|
||||
distributions. A Windows build is planned for the future, but not currently in
|
||||
development.
|
||||
|
||||
Running Interface
|
||||
-----
|
||||
|
||||
Using finder locate the interface.app Application in build/interface/Debug,
|
||||
double-click the icon, and wait for interface to launch. At this point you will
|
||||
connect to our default domain: "root.highfidelity.io".
|
||||
|
||||
I'm in-world, what can I do?
|
||||
----
|
||||
If you don't see anything, make sure your preferences are pointing to root.highfidelity.io,
|
||||
if you still have no luck it's possible our servers are simply down; if you're experiencing
|
||||
a major bug, let us know by suggesting a Job on Worklist.net -- make sure to include details
|
||||
about your operating system and your computer system.
|
||||
If you don't see anything, make sure your preferences are pointing to
|
||||
root.highfidelity.io, if you still have no luck it's possible our servers are
|
||||
simply down; if you're experiencing a major bug, let us know by suggesting a Job
|
||||
on Worklist.net -- make sure to include details about your operating system and
|
||||
your computer system.
|
||||
|
||||
To move around in-world, use the arrow keys (and Shift + up/down to fly up or down)
|
||||
or W A S D, and E or C to fly up/down. All of the other possible options and features
|
||||
are available via menus in the Interface application.
|
||||
To move around in-world, use the arrow keys (and Shift + up/down to fly up or
|
||||
down) or W A S D, and E or C to fly up/down. All of the other possible options
|
||||
and features are available via menus in the Interface application.
|
||||
|
||||
|
||||
Other components
|
||||
========
|
||||
|
||||
voxel-server, animation-server, audio-mixer, avatar-mixer, domain-server, pairing-server
|
||||
and space-server are architectural components that will allow you to run the full stack of
|
||||
the virtual world should you choose to.
|
||||
voxel-server, animation-server, audio-mixer, avatar-mixer, domain-server,
|
||||
pairing-server and space-server are architectural components that will allow
|
||||
you to run the full stack of the virtual world should you choose to.
|
||||
|
||||
|
||||
I want to run my own virtual world!
|
||||
========
|
||||
|
||||
In the voxel-server/src directory you will find a README that explains
|
||||
how to setup and administer a voxel-server.
|
||||
|
||||
Keep in mind that, at a minimum, you must run a domain-server, voxel-server,
|
||||
In order to set up your own virtual world, you need to set up and run your own
|
||||
local "domain". At a minimum, you must run a domain-server, voxel-server,
|
||||
audio-mixer, and avatar-mixer to have a working virtual world.
|
||||
Basic documentation for the other components is on its way.
|
||||
|
||||
Complete the steps above to build the system components. Then from the terminal
|
||||
window, change directory into the build direction, then launch the following
|
||||
components.
|
||||
|
||||
./domain-server/Debug/domain-server --local &
|
||||
./voxel-server/Debug/voxel-server --local &
|
||||
./avatar-mixer/Debug/avatar-mixer --local &
|
||||
./audio-mixer/Debug/audio-mixer --local &
|
||||
|
||||
To confirm that the components are running you can type the following command:
|
||||
|
||||
ps ax | grep -w "domain-server\|voxel-server\|audio-mixer\|avatar-mixer"
|
||||
|
||||
You should see something like this:
|
||||
|
||||
70488 s001 S 0:00.04 ./domain-server/Debug/domain-server --local
|
||||
70489 s001 S 0:00.23 ./voxel-server/Debug/voxel-server --local
|
||||
70490 s001 S 0:00.03 ./avatar-mixer/Debug/avatar-mixer --local
|
||||
70491 s001 S 0:00.48 ./audio-mixer/Debug/audio-mixer --local
|
||||
70511 s001 S+ 0:00.00 grep -w domain-server\|voxel-server\|audio-mixer\
|
||||
|avatar-mixer
|
||||
|
||||
Determine the IP address of the machine you're running these servers on. Here's
|
||||
a handy resource that explains how to do this for different operating systems.
|
||||
http://kb.iu.edu/data/aapa.html
|
||||
|
||||
On Mac OS X, and many Unix systems you can use the ifconfig command. Typically,
|
||||
the following command will give you the IP address you need to use.
|
||||
|
||||
ifconfig | grep inet | grep broadcast
|
||||
|
||||
You should get something like this:
|
||||
|
||||
inet 192.168.1.104 netmask 0xffffff00 broadcast 192.168.1.255
|
||||
|
||||
Your IP address is the first set of numbers. In this case "192.168.1.104". You
|
||||
may now use this IP address to access your domain. If you are running a local
|
||||
DNS or other name service you should be able to access this IP address by name
|
||||
as well.
|
||||
|
||||
To access your local domain in Interface, open the Preferences dialog box, from
|
||||
the Interface menu, and enter the IP address of the local DNS name for the
|
||||
server computer in the "Domain" edit control.
|
||||
|
||||
In the voxel-server/src directory you will find a README that explains in
|
||||
further detail how to setup and administer a voxel-server.
|
||||
|
||||
|
|
|
@ -82,7 +82,6 @@ const int STARTUP_JITTER_SAMPLES = PACKET_LENGTH_SAMPLES_PER_CHANNEL / 2;
|
|||
// Startup optimistically with small jitter buffer that
|
||||
// will start playback on the second received audio packet.
|
||||
|
||||
static const float CLIPBOARD_TREE_SCALE = 1.0f;
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) {
|
||||
fprintf(stdout, "%s", message.toLocal8Bit().constData());
|
||||
|
@ -97,7 +96,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_frameCount(0),
|
||||
_fps(120.0f),
|
||||
_justStarted(true),
|
||||
_clipboard(CLIPBOARD_TREE_SCALE),
|
||||
_voxelImporter(_window),
|
||||
_wantToKillLocalVoxels(false),
|
||||
_audioScope(256, 200, true),
|
||||
|
@ -237,6 +235,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
Application::~Application() {
|
||||
NodeList::getInstance()->removeHook(&_voxels);
|
||||
NodeList::getInstance()->removeHook(this);
|
||||
|
||||
_sharedVoxelSystem.changeTree(new VoxelTree);
|
||||
}
|
||||
|
||||
void Application::initializeGL() {
|
||||
|
@ -1041,10 +1041,8 @@ void Application::terminate() {
|
|||
|
||||
LeapManager::terminate();
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SettingsAutosave)) {
|
||||
Menu::getInstance()->saveSettings();
|
||||
_settings->sync();
|
||||
}
|
||||
Menu::getInstance()->saveSettings();
|
||||
_settings->sync();
|
||||
|
||||
if (_enableNetworkThread) {
|
||||
_stopNetworkReceiveThread = true;
|
||||
|
@ -1209,15 +1207,6 @@ void Application::exportVoxels() {
|
|||
void Application::importVoxels() {
|
||||
if (_voxelImporter.exec()) {
|
||||
qDebug("[DEBUG] Import succedded.\n");
|
||||
|
||||
if (_voxelImporter.getImportIntoClipboard()) {
|
||||
_clipboard.killLocalVoxels();
|
||||
_voxelImporter.getVoxelSystem()->copySubTreeIntoNewTree(
|
||||
_voxelImporter.getVoxelSystem()->getVoxelAt(0, 0, 0, 1),
|
||||
&_clipboard,
|
||||
true);
|
||||
_voxelImporter.reset();
|
||||
}
|
||||
} else {
|
||||
qDebug("[DEBUG] Import failed.\n");
|
||||
}
|
||||
|
@ -1232,13 +1221,17 @@ void Application::cutVoxels() {
|
|||
}
|
||||
|
||||
void Application::copyVoxels() {
|
||||
// switch to and clear the clipboard first...
|
||||
_sharedVoxelSystem.killLocalVoxels();
|
||||
if (_sharedVoxelSystem.getTree() != &_clipboard) {
|
||||
_clipboard.eraseAllVoxels();
|
||||
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||
}
|
||||
|
||||
// then copy onto it if there is something to copy
|
||||
VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
if (selectedNode) {
|
||||
// clear the clipboard first...
|
||||
_clipboard.killLocalVoxels();
|
||||
|
||||
// then copy onto it
|
||||
_voxels.copySubTreeIntoNewTree(selectedNode, &_clipboard, true);
|
||||
_voxels.copySubTreeIntoNewTree(selectedNode, &_sharedVoxelSystem, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1259,12 +1252,13 @@ void Application::pasteVoxels() {
|
|||
args.newBaseOctCode = calculatedOctCode = pointToVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
|
||||
}
|
||||
|
||||
if (_voxelImporter.getImportWaiting()) {
|
||||
_voxelImporter.getVoxelSystem()->recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
_voxelImporter.reset();
|
||||
} else {
|
||||
_clipboard.recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
_sharedVoxelSystem.getTree()->recurseTreeWithOperation(sendVoxelsOperation, &args);
|
||||
|
||||
if (_sharedVoxelSystem.getTree() != &_clipboard) {
|
||||
_sharedVoxelSystem.killLocalVoxels();
|
||||
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||
}
|
||||
|
||||
_voxelEditSender.flushQueue();
|
||||
|
||||
if (calculatedOctCode) {
|
||||
|
@ -1306,10 +1300,21 @@ void Application::initDisplay() {
|
|||
|
||||
void Application::init() {
|
||||
_voxels.init();
|
||||
_clipboard.init();
|
||||
_clipboardViewFrustum.setKeyholeRadius(1000.0f);
|
||||
_clipboardViewFrustum.calculate();
|
||||
_clipboard.setViewFrustum(&_clipboardViewFrustum);
|
||||
_sharedVoxelSystemViewFrustum.setPosition(glm::vec3(TREE_SCALE / 2.0f,
|
||||
TREE_SCALE / 2.0f,
|
||||
3.0f * TREE_SCALE / 2.0f));
|
||||
_sharedVoxelSystemViewFrustum.setNearClip(TREE_SCALE / 2.0f);
|
||||
_sharedVoxelSystemViewFrustum.setFarClip(3.0f * TREE_SCALE / 2.0f);
|
||||
_sharedVoxelSystemViewFrustum.setFieldOfView(90);
|
||||
_sharedVoxelSystemViewFrustum.setOrientation(glm::quat());
|
||||
_sharedVoxelSystemViewFrustum.calculate();
|
||||
_sharedVoxelSystem.setViewFrustum(&_sharedVoxelSystemViewFrustum);
|
||||
_sharedVoxelSystem.init();
|
||||
VoxelTree* tmpTree = _sharedVoxelSystem.getTree();
|
||||
_sharedVoxelSystem.changeTree(&_clipboard);
|
||||
delete tmpTree;
|
||||
|
||||
_voxelImporter.init();
|
||||
|
||||
_environment.init();
|
||||
|
||||
|
@ -1320,6 +1325,7 @@ void Application::init() {
|
|||
|
||||
_headMouseX = _mouseX = _glWidget->width() / 2;
|
||||
_headMouseY = _mouseY = _glWidget->height() / 2;
|
||||
QCursor::setPos(_headMouseX, _headMouseY);
|
||||
|
||||
_myAvatar.init();
|
||||
_myAvatar.setPosition(START_LOCATION);
|
||||
|
@ -1327,7 +1333,6 @@ void Application::init() {
|
|||
_myCamera.setModeShiftRate(1.0f);
|
||||
_myAvatar.setDisplayingLookatVectors(false);
|
||||
|
||||
QCursor::setPos(_headMouseX, _headMouseY);
|
||||
|
||||
OculusManager::connect();
|
||||
if (OculusManager::isConnected()) {
|
||||
|
@ -1384,7 +1389,9 @@ Avatar* Application::isLookingAtOtherAvatar(glm::vec3& mouseRayOrigin, glm::vec3
|
|||
if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) {
|
||||
Avatar* avatar = (Avatar *) node->getLinkedData();
|
||||
glm::vec3 headPosition = avatar->getHead().getPosition();
|
||||
if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition, HEAD_SPHERE_RADIUS * avatar->getScale())) {
|
||||
float distance;
|
||||
if (rayIntersectsSphere(mouseRayOrigin, mouseRayDirection, headPosition,
|
||||
HEAD_SPHERE_RADIUS * avatar->getScale(), distance)) {
|
||||
eyePosition = avatar->getHead().getEyePosition();
|
||||
_lookatIndicatorScale = avatar->getScale();
|
||||
_lookatOtherPosition = headPosition;
|
||||
|
@ -1722,6 +1729,40 @@ void Application::update(float deltaTime) {
|
|||
_myAvatar.simulate(deltaTime, NULL, Menu::getInstance()->getGyroCameraSensitivity());
|
||||
}
|
||||
|
||||
// no transmitter drive implies transmitter pick
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) {
|
||||
_transmitterPickStart = _myAvatar.getSkeleton().joint[AVATAR_JOINT_CHEST].position;
|
||||
glm::vec3 direction = _myAvatar.getOrientation() *
|
||||
glm::quat(glm::radians(_myTransmitter.getEstimatedRotation())) * IDENTITY_FRONT;
|
||||
|
||||
// check against voxels, avatars
|
||||
const float MAX_PICK_DISTANCE = 100.0f;
|
||||
float minDistance = MAX_PICK_DISTANCE;
|
||||
VoxelDetail detail;
|
||||
float distance;
|
||||
BoxFace face;
|
||||
if (_voxels.findRayIntersection(_transmitterPickStart, direction, detail, distance, face)) {
|
||||
minDistance = min(minDistance, distance);
|
||||
}
|
||||
for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
node->lock();
|
||||
if (node->getLinkedData() != NULL) {
|
||||
Avatar *avatar = (Avatar*)node->getLinkedData();
|
||||
if (!avatar->isInitialized()) {
|
||||
avatar->init();
|
||||
}
|
||||
if (avatar->findRayIntersection(_transmitterPickStart, direction, distance)) {
|
||||
minDistance = min(minDistance, distance);
|
||||
}
|
||||
}
|
||||
node->unlock();
|
||||
}
|
||||
_transmitterPickEnd = _transmitterPickStart + direction * minDistance;
|
||||
|
||||
} else {
|
||||
_transmitterPickStart = _transmitterPickEnd = glm::vec3();
|
||||
}
|
||||
|
||||
if (!OculusManager::isConnected()) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
|
@ -2191,15 +2232,11 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
glTranslatef(_mouseVoxel.x * TREE_SCALE,
|
||||
_mouseVoxel.y * TREE_SCALE,
|
||||
_mouseVoxel.z * TREE_SCALE);
|
||||
glScalef(_mouseVoxel.s * TREE_SCALE,
|
||||
_mouseVoxel.s * TREE_SCALE,
|
||||
_mouseVoxel.s * TREE_SCALE);
|
||||
glScalef(_mouseVoxel.s,
|
||||
_mouseVoxel.s,
|
||||
_mouseVoxel.s);
|
||||
|
||||
if (_voxelImporter.getImportWaiting()) {
|
||||
_voxelImporter.getVoxelSystem()->render(true);
|
||||
} else {
|
||||
_clipboard.render(true);
|
||||
}
|
||||
_sharedVoxelSystem.render(true);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
@ -2271,6 +2308,27 @@ void Application::displaySide(Camera& whichCamera) {
|
|||
}
|
||||
|
||||
renderFollowIndicator();
|
||||
|
||||
// render transmitter pick ray, if non-empty
|
||||
if (_transmitterPickStart != _transmitterPickEnd) {
|
||||
Glower glower;
|
||||
const float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f };
|
||||
glColor3fv(TRANSMITTER_PICK_COLOR);
|
||||
glLineWidth(3.0f);
|
||||
glBegin(GL_LINES);
|
||||
glVertex3f(_transmitterPickStart.x, _transmitterPickStart.y, _transmitterPickStart.z);
|
||||
glVertex3f(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z);
|
||||
glEnd();
|
||||
glLineWidth(1.0f);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z);
|
||||
|
||||
const float PICK_END_RADIUS = 0.025f;
|
||||
glutSolidSphere(PICK_END_RADIUS, 8, 8);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::displayOverlay() {
|
||||
|
|
|
@ -112,6 +112,8 @@ public:
|
|||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
VoxelSystem* getVoxels() { return &_voxels; }
|
||||
VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; }
|
||||
VoxelTree* getClipboard() { return &_clipboard; }
|
||||
Environment* getEnvironment() { return &_environment; }
|
||||
SerialInterface* getSerialHeadSensor() { return &_serialHeadSensor; }
|
||||
Webcam* getWebcam() { return &_webcam; }
|
||||
|
@ -238,10 +240,11 @@ private:
|
|||
|
||||
Stars _stars;
|
||||
|
||||
VoxelSystem _voxels;
|
||||
VoxelSystem _clipboard; // if I copy/paste
|
||||
ViewFrustum _clipboardViewFrustum;
|
||||
VoxelSystem _voxels;
|
||||
VoxelTree _clipboard; // if I copy/paste
|
||||
VoxelImporter _voxelImporter;
|
||||
VoxelSystem _sharedVoxelSystem;
|
||||
ViewFrustum _sharedVoxelSystemViewFrustum;
|
||||
|
||||
QByteArray _voxelsFilename;
|
||||
bool _wantToKillLocalVoxels;
|
||||
|
@ -298,6 +301,9 @@ private:
|
|||
glm::vec3 _lookatOtherPosition;
|
||||
float _lookatIndicatorScale;
|
||||
|
||||
glm::vec3 _transmitterPickStart;
|
||||
glm::vec3 _transmitterPickEnd;
|
||||
|
||||
bool _perfStatsOn; // Do we want to display perfStats?
|
||||
|
||||
ChatEntry _chatEntry; // chat entry field
|
||||
|
|
|
@ -34,9 +34,9 @@ const float FIELD_OF_VIEW = 60.0f;
|
|||
|
||||
class GLWidget : public QGLWidget {
|
||||
public:
|
||||
GLWidget(QWidget* parent = NULL, VoxelSystem* voxelSystem = NULL);
|
||||
GLWidget(QWidget* parent = NULL);
|
||||
void setDraw(bool draw) {_draw = draw;}
|
||||
void setTargetCenter(glm::vec3 targetCenter) {_targetCenter = targetCenter;}
|
||||
void setTargetCenter(glm::vec3 targetCenter) { _targetCenter = targetCenter; }
|
||||
|
||||
protected:
|
||||
virtual void initializeGL();
|
||||
|
@ -61,9 +61,8 @@ private:
|
|||
int _mouseY;
|
||||
};
|
||||
|
||||
GLWidget::GLWidget(QWidget *parent, VoxelSystem *voxelSystem)
|
||||
GLWidget::GLWidget(QWidget *parent)
|
||||
: QGLWidget(parent, Application::getInstance()->getGLWidget()),
|
||||
_voxelSystem(voxelSystem),
|
||||
_draw(false),
|
||||
_a(0.0f),
|
||||
_h(VERTICAL_ANGLE),
|
||||
|
@ -71,6 +70,7 @@ GLWidget::GLWidget(QWidget *parent, VoxelSystem *voxelSystem)
|
|||
_pressed(false),
|
||||
_mouseX(0),
|
||||
_mouseY(0) {
|
||||
_voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
}
|
||||
|
||||
void GLWidget::initializeGL() {
|
||||
|
@ -110,7 +110,7 @@ void GLWidget::paintGL() {
|
|||
UP_VECT.x, UP_VECT.y, UP_VECT.z);
|
||||
|
||||
|
||||
if (_draw && _voxelSystem) {
|
||||
if (_draw) {
|
||||
glBegin(GL_LINES);
|
||||
glColor3d(1, 1 ,1);
|
||||
glVertex3d(0, 0, 0);
|
||||
|
@ -134,11 +134,11 @@ void GLWidget::paintGL() {
|
|||
glVertex3d(2 * _targetCenter.x, 2 * _targetCenter.y, 0 );
|
||||
glEnd();
|
||||
|
||||
glScalef(1.0f / TREE_SCALE, 1.0f / TREE_SCALE, 1.0f / TREE_SCALE);
|
||||
_voxelSystem->render(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GLWidget::mousePressEvent(QMouseEvent* event) {
|
||||
_pressed = true;
|
||||
_mouseX = event->globalX();
|
||||
|
@ -158,14 +158,13 @@ void GLWidget::mouseReleaseEvent(QMouseEvent* event) {
|
|||
_pressed = false;
|
||||
}
|
||||
|
||||
ImportDialog::ImportDialog(QWidget *parent, VoxelSystem* voxelSystem)
|
||||
ImportDialog::ImportDialog(QWidget *parent)
|
||||
: QFileDialog(parent, WINDOW_NAME, DESKTOP_LOCATION, IMPORT_FILE_TYPES),
|
||||
_importButton (IMPORT_BUTTON_NAME, this),
|
||||
_clipboardImportBox(IMPORT_TO_CLIPBOARD_CHECKBOX_STRING, this),
|
||||
_previewBox (PREVIEW_CHECKBOX_STRING, this),
|
||||
_previewBar (this),
|
||||
_glPreview (new GLWidget(this, voxelSystem)) {
|
||||
|
||||
_glPreview (new GLWidget(this)) {
|
||||
setOption(QFileDialog::DontUseNativeDialog, true);
|
||||
setFileMode(QFileDialog::ExistingFile);
|
||||
setViewMode(QFileDialog::Detail);
|
||||
|
@ -188,15 +187,18 @@ ImportDialog::ImportDialog(QWidget *parent, VoxelSystem* voxelSystem)
|
|||
|
||||
connect(this, SIGNAL(currentChanged(QString)), SLOT(saveCurrentFile(QString)));
|
||||
connect(&_glTimer, SIGNAL(timeout()), SLOT(timer()));
|
||||
|
||||
connect(voxelSystem, SIGNAL(importSize(float,float,float)), SLOT(setGLCamera(float, float, float)));
|
||||
connect(voxelSystem, SIGNAL(importProgress(int)), &_previewBar, SLOT(setValue(int)));
|
||||
}
|
||||
|
||||
ImportDialog::~ImportDialog() {
|
||||
delete _glPreview;
|
||||
}
|
||||
|
||||
void ImportDialog::init() {
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
connect(voxelSystem, SIGNAL(importSize(float,float,float)), SLOT(setGLCamera(float,float,float)));
|
||||
connect(voxelSystem, SIGNAL(importProgress(int)), &_previewBar, SLOT(setValue(int)));
|
||||
}
|
||||
|
||||
void ImportDialog::import() {
|
||||
_importButton.setDisabled(true);
|
||||
_clipboardImportBox.setDisabled(true);
|
||||
|
|
|
@ -23,15 +23,16 @@ class GLWidget;
|
|||
class ImportDialog : public QFileDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ImportDialog(QWidget* parent = NULL, VoxelSystem* voxelSystem = NULL);
|
||||
ImportDialog(QWidget* parent = NULL);
|
||||
~ImportDialog();
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
|
||||
bool getWantPreview() const { return _previewBox.isChecked(); }
|
||||
QString getCurrentFile() const { return _currentFile; }
|
||||
bool getImportIntoClipboard() const { return _clipboardImportBox.isChecked(); }
|
||||
|
||||
void reset();
|
||||
|
||||
signals:
|
||||
void previewToggled(bool);
|
||||
void accepted();
|
||||
|
|
|
@ -52,12 +52,7 @@ Menu::Menu() :
|
|||
Application *appInstance = Application::getInstance();
|
||||
|
||||
QMenu* fileMenu = addMenu("File");
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Quit,
|
||||
Qt::CTRL | Qt::Key_Q,
|
||||
appInstance,
|
||||
SLOT(quit())))->setMenuRole(QAction::QuitRole);
|
||||
|
||||
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Preferences,
|
||||
Qt::CTRL | Qt::Key_Comma,
|
||||
|
@ -66,159 +61,89 @@ Menu::Menu() :
|
|||
|
||||
#if defined(Q_OS_MAC) && defined(QT_NO_DEBUG)
|
||||
// show "Check for Updates" in the menu
|
||||
(addActionToQMenuAndActionHash(fileMenu, MenuOption::CheckForUpdates, 0, this, SLOT(checkForUpdates())))->setMenuRole(QAction::ApplicationSpecificRole);
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::CheckForUpdates,
|
||||
0,
|
||||
this,
|
||||
SLOT(checkForUpdates())))->setMenuRole(QAction::ApplicationSpecificRole);
|
||||
#endif
|
||||
|
||||
QMenu* pairMenu = addMenu("Pair");
|
||||
addActionToQMenuAndActionHash(pairMenu, MenuOption::Pair, 0, PairingHandler::getInstance(), SLOT(sendPairRequest()));
|
||||
addDisabledActionAndSeparator(fileMenu, "Voxels");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ExportVoxels, Qt::CTRL | Qt::Key_E, appInstance, SLOT(exportVoxels()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ImportVoxels, Qt::CTRL | Qt::Key_I, appInstance, SLOT(importVoxels()));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Go");
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoHome,
|
||||
Qt::CTRL | Qt::Key_G,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(goHome()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoToDomain,
|
||||
Qt::CTRL | Qt::Key_D,
|
||||
this,
|
||||
SLOT(goToDomain()));
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::GoToLocation,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
this,
|
||||
SLOT(goToLocation()));
|
||||
|
||||
|
||||
QMenu* optionsMenu = addMenu("Options");
|
||||
addDisabledActionAndSeparator(fileMenu, "Settings");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::Mirror, Qt::Key_H);
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::GyroLook, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::HeadMouse);
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::TransmitterDrive, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, true);
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::TestPing, 0, true);
|
||||
addDisabledActionAndSeparator(fileMenu, "Devices");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Pair, 0, PairingHandler::getInstance(), SLOT(sendPairRequest()));
|
||||
addCheckableActionToQMenuAndActionHash(fileMenu, MenuOption::TransmitterDrive, 0, true);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu,
|
||||
MenuOption::Fullscreen,
|
||||
Qt::Key_F,
|
||||
false,
|
||||
appInstance,
|
||||
SLOT(setFullscreen(bool)));
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Quit,
|
||||
Qt::CTRL | Qt::Key_Q,
|
||||
appInstance,
|
||||
SLOT(quit())))->setMenuRole(QAction::QuitRole);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu,
|
||||
MenuOption::Webcam,
|
||||
0,
|
||||
false,
|
||||
appInstance->getWebcam(),
|
||||
SLOT(setEnabled(bool)));
|
||||
QMenu* editMenu = addMenu("Edit");
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::CutVoxels, Qt::CTRL | Qt::Key_X, appInstance, SLOT(cutVoxels()));
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels()));
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu,
|
||||
MenuOption::SkeletonTracking,
|
||||
0,
|
||||
false,
|
||||
appInstance->getWebcam(),
|
||||
SLOT(setSkeletonTrackingOn(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu,
|
||||
addDisabledActionAndSeparator(editMenu, "Physics");
|
||||
addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, true);
|
||||
addCheckableActionToQMenuAndActionHash(editMenu,
|
||||
MenuOption::Collisions,
|
||||
0,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantCollisionsOn(bool)));
|
||||
|
||||
addActionToQMenuAndActionHash(optionsMenu,
|
||||
MenuOption::WebcamMode,
|
||||
0,
|
||||
appInstance->getWebcam()->getGrabber(),
|
||||
SLOT(cycleVideoSendMode()));
|
||||
addCheckableActionToQMenuAndActionHash(optionsMenu,
|
||||
MenuOption::WebcamTexture,
|
||||
0,
|
||||
false,
|
||||
appInstance->getWebcam()->getGrabber(),
|
||||
SLOT(setDepthOnly(bool)));
|
||||
|
||||
addActionToQMenuAndActionHash(optionsMenu,
|
||||
MenuOption::GoHome,
|
||||
Qt::CTRL | Qt::Key_G,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(goHome()));
|
||||
|
||||
QMenu* audioMenu = addMenu("Audio");
|
||||
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::EchoAudio);
|
||||
|
||||
QMenu* renderMenu = addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu,
|
||||
MenuOption::Voxels,
|
||||
Qt::SHIFT | Qt::Key_V,
|
||||
true,
|
||||
appInstance,
|
||||
SLOT(setRenderVoxels(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::VoxelTextures);
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::Stars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::GroundPlane, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::Avatars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::AvatarAsBalls);
|
||||
|
||||
addActionToQMenuAndActionHash(renderMenu,
|
||||
MenuOption::VoxelMode,
|
||||
0,
|
||||
appInstance->getAvatar()->getVoxels(),
|
||||
SLOT(cycleMode()));
|
||||
|
||||
addActionToQMenuAndActionHash(renderMenu,
|
||||
MenuOption::FaceMode,
|
||||
0,
|
||||
&appInstance->getAvatar()->getHead().getFace(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
addActionToQMenuAndActionHash(renderMenu,
|
||||
MenuOption::GlowMode,
|
||||
0,
|
||||
appInstance->getGlowEffect(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::AmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::FrameTimer);
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::LookAtVectors, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::LookAtIndicator, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::FirstPerson, Qt::Key_P, true);
|
||||
|
||||
addActionToQMenuAndActionHash(renderMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
Qt::Key_Plus,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(increaseSize()));
|
||||
addActionToQMenuAndActionHash(renderMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
Qt::Key_Minus,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(decreaseSize()));
|
||||
addActionToQMenuAndActionHash(renderMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
0,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(resetSize()));
|
||||
|
||||
QMenu* toolsMenu = addMenu("Tools");
|
||||
addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Stats, Qt::Key_Slash);
|
||||
addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L);
|
||||
addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Oscilloscope, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Bandwidth, 0, true);
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails()));
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelStats, 0, this, SLOT(voxelStatsDetails()));
|
||||
|
||||
|
||||
QMenu* voxelMenu = addMenu("Voxels");
|
||||
_voxelModeActionsGroup = new QActionGroup(this);
|
||||
_voxelModeActionsGroup->setExclusive(false);
|
||||
|
||||
QAction* addVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelAddMode, Qt::Key_V);
|
||||
QAction* addVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelAddMode, Qt::Key_V);
|
||||
_voxelModeActionsGroup->addAction(addVoxelMode);
|
||||
|
||||
QAction* deleteVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelDeleteMode, Qt::Key_R);
|
||||
QAction* deleteVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelDeleteMode, Qt::Key_R);
|
||||
_voxelModeActionsGroup->addAction(deleteVoxelMode);
|
||||
|
||||
QAction* colorVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelColorMode, Qt::Key_B);
|
||||
QAction* colorVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelColorMode, Qt::Key_B);
|
||||
_voxelModeActionsGroup->addAction(colorVoxelMode);
|
||||
|
||||
QAction* selectVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelSelectMode, Qt::Key_O);
|
||||
QAction* selectVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelSelectMode, Qt::Key_O);
|
||||
_voxelModeActionsGroup->addAction(selectVoxelMode);
|
||||
|
||||
QAction* getColorMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelGetColorMode, Qt::Key_G);
|
||||
QAction* getColorMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelGetColorMode, Qt::Key_G);
|
||||
_voxelModeActionsGroup->addAction(getColorMode);
|
||||
|
||||
// connect each of the voxel mode actions to the updateVoxelModeActionsSlot
|
||||
foreach (QAction* action, _voxelModeActionsGroup->actions()) {
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(updateVoxelModeActions()));
|
||||
}
|
||||
|
||||
QAction* voxelPaintColor = addActionToQMenuAndActionHash(voxelMenu,
|
||||
|
||||
QAction* voxelPaintColor = addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::VoxelPaintColor,
|
||||
Qt::META | Qt::Key_C,
|
||||
this,
|
||||
|
@ -230,29 +155,106 @@ Menu::Menu() :
|
|||
voxelPaintColor->setData(paintColor);
|
||||
voxelPaintColor->setIcon(Swatch::createIcon(paintColor));
|
||||
|
||||
addActionToQMenuAndActionHash(voxelMenu,
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::DecreaseVoxelSize,
|
||||
QKeySequence::ZoomOut,
|
||||
appInstance,
|
||||
SLOT(decreaseVoxelSize()));
|
||||
addActionToQMenuAndActionHash(voxelMenu,
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::IncreaseVoxelSize,
|
||||
QKeySequence::ZoomIn,
|
||||
appInstance,
|
||||
SLOT(increaseVoxelSize()));
|
||||
addActionToQMenuAndActionHash(voxelMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::DestructiveAddVoxel);
|
||||
|
||||
addActionToQMenuAndActionHash(voxelMenu, MenuOption::ExportVoxels, Qt::CTRL | Qt::Key_E, appInstance, SLOT(exportVoxels()));
|
||||
addActionToQMenuAndActionHash(voxelMenu, MenuOption::ImportVoxels, Qt::CTRL | Qt::Key_I, appInstance, SLOT(importVoxels()));
|
||||
addActionToQMenuAndActionHash(voxelMenu, MenuOption::CutVoxels, Qt::CTRL | Qt::Key_X, appInstance, SLOT(cutVoxels()));
|
||||
addActionToQMenuAndActionHash(voxelMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels()));
|
||||
addActionToQMenuAndActionHash(voxelMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels()));
|
||||
|
||||
QMenu* debugMenu = addMenu("Debug");
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors()));
|
||||
|
||||
QMenu* frustumMenu = debugMenu->addMenu("View Frustum Debugging Tools");
|
||||
|
||||
QMenu* viewMenu = addMenu("View");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::Fullscreen,
|
||||
Qt::Key_F,
|
||||
false,
|
||||
appInstance,
|
||||
SLOT(setFullscreen(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true);
|
||||
|
||||
addActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
Qt::Key_Plus,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(increaseSize()));
|
||||
addActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
Qt::Key_Minus,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(decreaseSize()));
|
||||
addActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
0,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(resetSize()));
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::Key_H);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::SkeletonTracking,
|
||||
0,
|
||||
false,
|
||||
appInstance->getWebcam(),
|
||||
SLOT(setSkeletonTrackingOn(bool)));
|
||||
|
||||
addDisabledActionAndSeparator(viewMenu, "Stats");
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true);
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails()));
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::VoxelStats, 0, this, SLOT(voxelStatsDetails()));
|
||||
|
||||
QMenu* developerMenu = addMenu("Developer");
|
||||
addDisabledActionAndSeparator(developerMenu, "Rendering");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::Voxels,
|
||||
Qt::SHIFT | Qt::Key_V,
|
||||
true,
|
||||
appInstance,
|
||||
SLOT(setRenderVoxels(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::VoxelTextures);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::GroundPlane, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Avatars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AvatarAsBalls);
|
||||
|
||||
addActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::VoxelMode,
|
||||
0,
|
||||
appInstance->getAvatar()->getVoxels(),
|
||||
SLOT(cycleMode()));
|
||||
|
||||
addActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::FaceMode,
|
||||
0,
|
||||
&appInstance->getAvatar()->getHead().getFace(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
addActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::GlowMode,
|
||||
0,
|
||||
appInstance->getGlowEffect(),
|
||||
SLOT(cycleRenderMode()));
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtVectors, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtIndicator, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::FrameTimer);
|
||||
|
||||
addDisabledActionAndSeparator(developerMenu, "Testing");
|
||||
|
||||
QMenu* frustumMenu = developerMenu->addMenu("View Frustum Debugging Tools");
|
||||
addCheckableActionToQMenuAndActionHash(frustumMenu, MenuOption::DisplayFrustum, Qt::SHIFT | Qt::Key_F);
|
||||
|
||||
addActionToQMenuAndActionHash(frustumMenu,
|
||||
|
@ -262,14 +264,14 @@ Menu::Menu() :
|
|||
SLOT(cycleFrustumRenderMode()));
|
||||
updateFrustumRenderModeAction();
|
||||
|
||||
addActionToQMenuAndActionHash(debugMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests()));
|
||||
addActionToQMenuAndActionHash(debugMenu,
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests()));
|
||||
addActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::TreeStats,
|
||||
Qt::SHIFT | Qt::Key_S,
|
||||
appInstance->getVoxels(),
|
||||
SLOT(collectStatsForTreesAndVBOs()));
|
||||
|
||||
QMenu* renderDebugMenu = debugMenu->addMenu("Render Debugging Tools");
|
||||
QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools");
|
||||
addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings);
|
||||
|
||||
addActionToQMenuAndActionHash(renderDebugMenu,
|
||||
|
@ -330,41 +332,29 @@ Menu::Menu() :
|
|||
Qt::CTRL | Qt::Key_T,
|
||||
appInstance->getVoxels(),
|
||||
SLOT(trueColorize()));
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(debugMenu,
|
||||
MenuOption::SendVoxelColors,
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::Webcam,
|
||||
0,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantColor(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(debugMenu,
|
||||
MenuOption::LowRes,
|
||||
false,
|
||||
appInstance->getWebcam(),
|
||||
SLOT(setEnabled(bool)));
|
||||
|
||||
addActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::WebcamMode,
|
||||
0,
|
||||
appInstance->getWebcam()->getGrabber(),
|
||||
SLOT(cycleVideoSendMode()));
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::WebcamTexture,
|
||||
0,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantLowResMoving(bool)));
|
||||
false,
|
||||
appInstance->getWebcam()->getGrabber(),
|
||||
SLOT(setDepthOnly(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(debugMenu,
|
||||
MenuOption::DeltaSending,
|
||||
0,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantDelta(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(debugMenu,
|
||||
MenuOption::OcclusionCulling,
|
||||
Qt::SHIFT | Qt::Key_C,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantOcclusionCulling(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::CoverageMap, Qt::SHIFT | Qt::CTRL | Qt::Key_O);
|
||||
addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P);
|
||||
addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::SimulateLeapHand);
|
||||
addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::TestRaveGlove);
|
||||
|
||||
QMenu* audioDebugMenu = debugMenu->addMenu("Audio Debugging Tools");
|
||||
QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools");
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoAudio);
|
||||
addActionToQMenuAndActionHash(audioDebugMenu,
|
||||
MenuOption::ListenModeNormal,
|
||||
Qt::CTRL | Qt::Key_1,
|
||||
|
@ -381,12 +371,46 @@ Menu::Menu() :
|
|||
appInstance,
|
||||
SLOT(setListenModeSingleSource()));
|
||||
|
||||
QMenu* settingsMenu = addMenu("Settings");
|
||||
addCheckableActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsAutosave, 0, true);
|
||||
addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsLoad, 0, this, SLOT(loadSettings()));
|
||||
addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsSave, 0, this, SLOT(saveSettings()));
|
||||
addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings()));
|
||||
addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings()));
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::TestPing, 0, true);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::SendVoxelColors,
|
||||
0,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantColor(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::LowRes,
|
||||
0,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantLowResMoving(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::DeltaSending,
|
||||
0,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantDelta(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu,
|
||||
MenuOption::OcclusionCulling,
|
||||
Qt::SHIFT | Qt::Key_C,
|
||||
true,
|
||||
appInstance->getAvatar(),
|
||||
SLOT(setWantOcclusionCulling(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::CoverageMap, Qt::SHIFT | Qt::CTRL | Qt::Key_O);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::SimulateLeapHand);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::TestRaveGlove);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::GyroLook, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::HeadMouse);
|
||||
|
||||
addDisabledActionAndSeparator(developerMenu, "Voxels");
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DestructiveAddVoxel);
|
||||
}
|
||||
|
||||
void Menu::loadSettings(QSettings* settings) {
|
||||
|
@ -553,6 +577,11 @@ void Menu::handleViewFrustumOffsetKeyModifier(int key) {
|
|||
}
|
||||
}
|
||||
|
||||
void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName) {
|
||||
destinationMenu->addSeparator();
|
||||
(destinationMenu->addAction(actionName))->setEnabled(false);
|
||||
}
|
||||
|
||||
QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
const QString actionName,
|
||||
const QKeySequence& shortcut,
|
||||
|
@ -607,7 +636,7 @@ bool Menu::isVoxelModeActionChecked() {
|
|||
}
|
||||
|
||||
void Menu::editPreferences() {
|
||||
Application *applicationInstance = Application::getInstance();
|
||||
Application* applicationInstance = Application::getInstance();
|
||||
QDialog dialog(applicationInstance->getGLWidget());
|
||||
dialog.setWindowTitle("Interface Preferences");
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
|
@ -651,9 +680,11 @@ void Menu::editPreferences() {
|
|||
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
int ret = dialog.exec();
|
||||
applicationInstance->getWindow()->activateWindow();
|
||||
if (ret != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray newHostname;
|
||||
|
||||
|
@ -698,6 +729,118 @@ void Menu::editPreferences() {
|
|||
applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height());
|
||||
}
|
||||
|
||||
void Menu::goToDomain() {
|
||||
Application* applicationInstance = Application::getInstance();
|
||||
QDialog dialog(applicationInstance->getGLWidget());
|
||||
dialog.setWindowTitle("Go To Domain");
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
dialog.setLayout(layout);
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
const int QLINE_MINIMUM_WIDTH = 400;
|
||||
|
||||
QLineEdit* domainServerHostname = new QLineEdit(QString(NodeList::getInstance()->getDomainHostname()));
|
||||
domainServerHostname->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Domain server:", domainServerHostname);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
int ret = dialog.exec();
|
||||
applicationInstance->getWindow()->activateWindow();
|
||||
if (ret != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray newHostname;
|
||||
|
||||
if (domainServerHostname->text().size() > 0) {
|
||||
// the user input a new hostname, use that
|
||||
newHostname = domainServerHostname->text().toLocal8Bit();
|
||||
} else {
|
||||
// the user left the field blank, use the default hostname
|
||||
newHostname = QByteArray(DEFAULT_DOMAIN_HOSTNAME);
|
||||
}
|
||||
|
||||
// check if the domain server hostname is new
|
||||
if (memcmp(NodeList::getInstance()->getDomainHostname(), newHostname.constData(), newHostname.size()) != 0) {
|
||||
|
||||
NodeList::getInstance()->clear();
|
||||
|
||||
// kill the local voxels
|
||||
applicationInstance->getVoxels()->killLocalVoxels();
|
||||
|
||||
// reset the environment to default
|
||||
applicationInstance->getEnvironment()->resetToDefault();
|
||||
|
||||
// set the new hostname
|
||||
NodeList::getInstance()->setDomainHostname(newHostname.constData());
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::goToLocation() {
|
||||
Application* applicationInstance = Application::getInstance();
|
||||
QDialog dialog(applicationInstance->getGLWidget());
|
||||
dialog.setWindowTitle("Go To Location");
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
dialog.setLayout(layout);
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
const int QLINE_MINIMUM_WIDTH = 300;
|
||||
|
||||
Application* appInstance = Application::getInstance();
|
||||
MyAvatar* myAvatar = appInstance->getAvatar();
|
||||
glm::vec3 avatarPos = myAvatar->getPosition();
|
||||
QString currentLocation = QString("%1, %2, %3").arg(QString::number(avatarPos.x),
|
||||
QString::number(avatarPos.y), QString::number(avatarPos.z));
|
||||
|
||||
QLineEdit* coordinates = new QLineEdit(currentLocation);
|
||||
coordinates->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Coordinates as x,y,z:", coordinates);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
int ret = dialog.exec();
|
||||
applicationInstance->getWindow()->activateWindow();
|
||||
if (ret != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray newCoordinates;
|
||||
|
||||
if (coordinates->text().size() > 0) {
|
||||
// the user input a new hostname, use that
|
||||
|
||||
QString delimiterPattern(",");
|
||||
QStringList coordinateItems = coordinates->text().split(delimiterPattern);
|
||||
|
||||
const int NUMBER_OF_COORDINATE_ITEMS = 3;
|
||||
const int X_ITEM = 0;
|
||||
const int Y_ITEM = 1;
|
||||
const int Z_ITEM = 2;
|
||||
if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) {
|
||||
double x = coordinateItems[X_ITEM].toDouble();
|
||||
double y = coordinateItems[Y_ITEM].toDouble();
|
||||
double z = coordinateItems[Z_ITEM].toDouble();
|
||||
glm::vec3 newAvatarPos(x, y, z);
|
||||
|
||||
if (newAvatarPos != avatarPos) {
|
||||
qDebug("Going To Location: %f, %f, %f...\n", x, y, z);
|
||||
myAvatar->setPosition(newAvatarPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Menu::bandwidthDetails() {
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void editPreferences();
|
||||
void goToDomain();
|
||||
void goToLocation();
|
||||
void bandwidthDetailsClosed();
|
||||
void voxelStatsDetailsClosed();
|
||||
void cycleFrustumRenderMode();
|
||||
|
@ -81,6 +83,8 @@ private:
|
|||
void scanMenuBar(settingsAction modifySetting, QSettings* set);
|
||||
void scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set);
|
||||
|
||||
/// helper method to have separators with labels that are also compatible with OS X
|
||||
void addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName);
|
||||
QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu,
|
||||
const QString actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
|
@ -116,10 +120,10 @@ namespace MenuOption {
|
|||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString CheckForUpdates = "Check for Updates...";
|
||||
const QString Collisions = "Collisions";
|
||||
const QString CopyVoxels = "Copy Voxels";
|
||||
const QString CopyVoxels = "Copy";
|
||||
const QString CoverageMap = "Render Coverage Map";
|
||||
const QString CoverageMapV2 = "Render Coverage Map V2";
|
||||
const QString CutVoxels = "Cut Voxels";
|
||||
const QString CutVoxels = "Cut";
|
||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
||||
const QString DestructiveAddVoxel = "Create Voxel is Destructive";
|
||||
|
@ -141,6 +145,8 @@ namespace MenuOption {
|
|||
const QString FrustumRenderMode = "Render Mode";
|
||||
const QString Fullscreen = "Fullscreen";
|
||||
const QString GlowMode = "Cycle Glow Mode";
|
||||
const QString GoToDomain = "Go To Domain...";
|
||||
const QString GoToLocation = "Go To Location...";
|
||||
const QString ImportVoxels = "Import Voxels";
|
||||
const QString ImportVoxelsClipboard = "Import Voxels to Clipboard";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
|
@ -161,7 +167,7 @@ namespace MenuOption {
|
|||
const QString OcclusionCulling = "Occlusion Culling";
|
||||
const QString Oscilloscope = "Audio Oscilloscope";
|
||||
const QString Pair = "Pair";
|
||||
const QString PasteVoxels = "Paste Voxels";
|
||||
const QString PasteVoxels = "Paste";
|
||||
const QString PipelineWarnings = "Show Render Pipeline Warnings";
|
||||
const QString Preferences = "Preferences...";
|
||||
const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors";
|
||||
|
@ -169,9 +175,6 @@ namespace MenuOption {
|
|||
const QString ResetSwatchColors = "Reset Swatch Colors";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString SendVoxelColors = "Colored Voxels";
|
||||
const QString SettingsAutosave = "Autosave";
|
||||
const QString SettingsLoad = "Load Settings";
|
||||
const QString SettingsSave = "Save Settings";
|
||||
const QString SettingsImport = "Import Settings";
|
||||
const QString SettingsExport = "Export Settings";
|
||||
const QString ShowTrueColors = "Show TRUE Colors";
|
||||
|
|
|
@ -46,7 +46,6 @@ void OculusManager::updateYawOffset() {
|
|||
}
|
||||
|
||||
void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) {
|
||||
yaw = pitch = roll = 0.0f;
|
||||
#ifdef __APPLE__
|
||||
_sensorFusion.GetOrientation().GetEulerAngles<Axis_Y, Axis_X, Axis_Z, Rotate_CCW, Handed_R>(&yaw, &pitch, &roll);
|
||||
|
||||
|
|
|
@ -125,9 +125,15 @@ void SerialInterface::initializePort(char* portname) {
|
|||
tcflush(_serialDescriptor, TCIOFLUSH);
|
||||
|
||||
// this disables streaming so there's no garbage data on reads
|
||||
write(_serialDescriptor, "SD\n", 3);
|
||||
if (write(_serialDescriptor, "SD\n", 3) != 3) {
|
||||
qDebug("Failed.\n");
|
||||
return;
|
||||
}
|
||||
char result[4];
|
||||
read(_serialDescriptor, result, 4);
|
||||
if (read(_serialDescriptor, result, 4) != 4) {
|
||||
qDebug("Failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tty_set_file_descriptor(_serialDescriptor);
|
||||
mpu_init(0);
|
||||
|
|
|
@ -598,13 +598,35 @@ float loadSetting(QSettings* settings, const char* name, float defaultValue) {
|
|||
return value;
|
||||
}
|
||||
|
||||
bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius) {
|
||||
glm::vec3 vecFromRayToSphereCenter = sphereCenter - rayStarting;
|
||||
double projection = glm::dot(vecFromRayToSphereCenter, rayNormalizedDirection);
|
||||
double shortestDistance = sqrt(glm::dot(vecFromRayToSphereCenter, vecFromRayToSphereCenter) - projection * projection);
|
||||
if (shortestDistance <= sphereRadius) {
|
||||
return true;
|
||||
bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,
|
||||
const glm::vec3& sphereCenter, float sphereRadius, float& distance) {
|
||||
glm::vec3 relativeOrigin = rayStarting - sphereCenter;
|
||||
|
||||
// compute the b, c terms of the quadratic equation (a is dot(direction, direction), which is one)
|
||||
float b = 2.0f * glm::dot(rayNormalizedDirection, relativeOrigin);
|
||||
float c = glm::dot(relativeOrigin, relativeOrigin) - sphereRadius * sphereRadius;
|
||||
|
||||
// compute the radicand of the quadratic. if less than zero, there's no intersection
|
||||
float radicand = b * b - 4.0f * c;
|
||||
if (radicand < 0.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute the first solution of the quadratic
|
||||
float root = sqrtf(radicand);
|
||||
float firstSolution = -b - root;
|
||||
if (firstSolution > 0.0f) {
|
||||
distance = firstSolution / 2.0f;
|
||||
return true; // origin is outside the sphere
|
||||
}
|
||||
|
||||
// now try the second solution
|
||||
float secondSolution = -b + root;
|
||||
if (secondSolution > 0.0f) {
|
||||
distance = 0.0f;
|
||||
return true; // origin is inside the sphere
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -615,4 +637,4 @@ bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadiu
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ void runTimingTests();
|
|||
|
||||
float loadSetting(QSettings* settings, const char* name, float defaultValue);
|
||||
|
||||
bool rayIntersectsSphere(glm::vec3& rayStarting, glm::vec3& rayNormalizedDirection, glm::vec3& sphereCenter, double sphereRadius);
|
||||
bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection,
|
||||
const glm::vec3& sphereCenter, float sphereRadius, float& distance);
|
||||
|
||||
bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadius);
|
||||
|
||||
|
|
|
@ -7,29 +7,24 @@
|
|||
//
|
||||
|
||||
#include <VoxelImporter.h>
|
||||
#include <Application.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QThreadPool>
|
||||
|
||||
static const int IMPORT_SYSTEM_SCALE = 1.0f;
|
||||
static const int MAX_VOXELS_PER_IMPORT = 2000000;
|
||||
|
||||
class ImportTask : public QObject, public QRunnable {
|
||||
public:
|
||||
ImportTask(VoxelSystem* voxelSystem, const QString &filename);
|
||||
ImportTask(const QString &filename);
|
||||
void run();
|
||||
|
||||
private:
|
||||
VoxelSystem* _voxelSystem;
|
||||
QString _filename;
|
||||
QString _filename;
|
||||
};
|
||||
|
||||
VoxelImporter::VoxelImporter(QWidget* parent)
|
||||
: QObject(parent),
|
||||
_voxelSystem(IMPORT_SYSTEM_SCALE, MAX_VOXELS_PER_IMPORT),
|
||||
_initialized(false),
|
||||
_importWaiting(false),
|
||||
_importDialog(parent, &_voxelSystem),
|
||||
_voxelTree(true),
|
||||
_importDialog(parent),
|
||||
_currentTask(NULL),
|
||||
_nextTask(NULL) {
|
||||
|
||||
|
@ -38,6 +33,10 @@ VoxelImporter::VoxelImporter(QWidget* parent)
|
|||
connect(&_importDialog, SIGNAL(accepted()), SLOT(import()));
|
||||
}
|
||||
|
||||
void VoxelImporter::init() {
|
||||
_importDialog.init();
|
||||
}
|
||||
|
||||
VoxelImporter::~VoxelImporter() {
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
|
@ -46,16 +45,15 @@ VoxelImporter::~VoxelImporter() {
|
|||
|
||||
if (_currentTask) {
|
||||
disconnect(_currentTask, 0, 0, 0);
|
||||
_voxelSystem.cancelImport();
|
||||
_voxelTree.cancelImport();
|
||||
_currentTask = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelImporter::reset() {
|
||||
_voxelSystem.killLocalVoxels();
|
||||
_voxelTree.eraseAllVoxels();
|
||||
_importDialog.reset();
|
||||
_filename = "";
|
||||
_importWaiting = false;
|
||||
|
||||
if (_nextTask) {
|
||||
delete _nextTask;
|
||||
|
@ -63,17 +61,11 @@ void VoxelImporter::reset() {
|
|||
}
|
||||
|
||||
if (_currentTask) {
|
||||
_voxelSystem.cancelImport();
|
||||
_voxelTree.cancelImport();
|
||||
}
|
||||
}
|
||||
|
||||
int VoxelImporter::exec() {
|
||||
if (!_initialized) {
|
||||
_voxelSystem.init();
|
||||
_importViewFrustum.calculate();
|
||||
_voxelSystem.setViewFrustum(&_importViewFrustum);
|
||||
_initialized = true;
|
||||
}
|
||||
reset();
|
||||
|
||||
int ret = _importDialog.exec();
|
||||
|
@ -82,7 +74,15 @@ int VoxelImporter::exec() {
|
|||
reset();
|
||||
} else {
|
||||
_importDialog.reset();
|
||||
_importWaiting = true;
|
||||
|
||||
if (_importDialog.getImportIntoClipboard()) {
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
|
||||
voxelSystem->copySubTreeIntoNewTree(voxelSystem->getTree()->rootNode,
|
||||
Application::getInstance()->getClipboard(),
|
||||
true);
|
||||
voxelSystem->changeTree(Application::getInstance()->getClipboard());
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -102,11 +102,11 @@ int VoxelImporter::preImport() {
|
|||
delete _nextTask;
|
||||
}
|
||||
|
||||
_nextTask = new ImportTask(&_voxelSystem, _filename);
|
||||
_nextTask = new ImportTask(_filename);
|
||||
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
|
||||
|
||||
if (_currentTask != NULL) {
|
||||
_voxelSystem.cancelImport();
|
||||
_voxelTree.cancelImport();
|
||||
} else {
|
||||
launchTask();
|
||||
}
|
||||
|
@ -138,12 +138,12 @@ int VoxelImporter::import() {
|
|||
delete _nextTask;
|
||||
}
|
||||
|
||||
_nextTask = new ImportTask(&_voxelSystem, _filename);
|
||||
_nextTask = new ImportTask(_filename);
|
||||
connect(_nextTask, SIGNAL(destroyed()), SLOT(launchTask()));
|
||||
connect(_nextTask, SIGNAL(destroyed()), &_importDialog, SLOT(accept()));
|
||||
|
||||
if (_currentTask != NULL) {
|
||||
_voxelSystem.cancelImport();
|
||||
_voxelTree.cancelImport();
|
||||
} else {
|
||||
launchTask();
|
||||
}
|
||||
|
@ -153,28 +153,36 @@ int VoxelImporter::import() {
|
|||
|
||||
void VoxelImporter::launchTask() {
|
||||
if (_nextTask != NULL) {
|
||||
_voxelSystem.killLocalVoxels();
|
||||
_currentTask = _nextTask;
|
||||
_nextTask = NULL;
|
||||
|
||||
if (Application::getInstance()->getSharedVoxelSystem()->getTree() != &_voxelTree) {
|
||||
Application::getInstance()->getSharedVoxelSystem()->changeTree(&_voxelTree);
|
||||
}
|
||||
|
||||
QThreadPool::globalInstance()->start(_currentTask);
|
||||
} else {
|
||||
_currentTask = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ImportTask::ImportTask(VoxelSystem* voxelSystem, const QString &filename)
|
||||
: _voxelSystem(voxelSystem),
|
||||
_filename(filename) {
|
||||
ImportTask::ImportTask(const QString &filename)
|
||||
: _filename(filename) {
|
||||
}
|
||||
|
||||
void ImportTask::run() {
|
||||
VoxelSystem* voxelSystem = Application::getInstance()->getSharedVoxelSystem();
|
||||
voxelSystem->killLocalVoxels();
|
||||
|
||||
if (_filename.endsWith(".png", Qt::CaseInsensitive)) {
|
||||
_voxelSystem->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data());
|
||||
voxelSystem->readFromSquareARGB32Pixels(_filename.toLocal8Bit().data());
|
||||
} else if (_filename.endsWith(".svo", Qt::CaseInsensitive)) {
|
||||
_voxelSystem->readFromSVOFile(_filename.toLocal8Bit().data());
|
||||
voxelSystem->readFromSVOFile(_filename.toLocal8Bit().data());
|
||||
} else if (_filename.endsWith(".schematic", Qt::CaseInsensitive)) {
|
||||
_voxelSystem->readFromSchematicFile(_filename.toLocal8Bit().data());
|
||||
voxelSystem->readFromSchematicFile(_filename.toLocal8Bit().data());
|
||||
} else {
|
||||
qDebug("[ERROR] Invalid file extension.\n");
|
||||
}
|
||||
|
||||
voxelSystem->getTree()->reaverageVoxelColors(voxelSystem->getTree()->rootNode);
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@ class VoxelImporter : public QObject {
|
|||
public:
|
||||
VoxelImporter(QWidget* parent = NULL);
|
||||
~VoxelImporter();
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
|
||||
bool getImportWaiting() const { return _importWaiting; }
|
||||
VoxelSystem* getVoxelSystem() { return &_voxelSystem; }
|
||||
bool getImportIntoClipboard() const { return _importDialog.getImportIntoClipboard(); }
|
||||
VoxelTree* getVoxelTree() { return &_voxelTree; }
|
||||
|
||||
public slots:
|
||||
int exec();
|
||||
|
@ -37,14 +37,10 @@ private slots:
|
|||
void launchTask();
|
||||
|
||||
private:
|
||||
VoxelSystem _voxelSystem;
|
||||
ViewFrustum _importViewFrustum;
|
||||
bool _initialized;
|
||||
bool _importWaiting;
|
||||
|
||||
VoxelTree _voxelTree;
|
||||
ImportDialog _importDialog;
|
||||
|
||||
QString _filename;
|
||||
QString _filename;
|
||||
|
||||
ImportTask* _currentTask;
|
||||
ImportTask* _nextTask;
|
||||
|
|
|
@ -115,6 +115,10 @@ void VoxelSystem::clearFreeBufferIndexes() {
|
|||
}
|
||||
|
||||
VoxelSystem::~VoxelSystem() {
|
||||
glDeleteBuffers(1, &_vboVerticesID);
|
||||
glDeleteBuffers(1, &_vboNormalsID);
|
||||
glDeleteBuffers(1, &_vboColorsID);
|
||||
glDeleteBuffers(1, &_vboIndicesID);
|
||||
delete[] _readVerticesArray;
|
||||
delete[] _writeVerticesArray;
|
||||
delete[] _readColorsArray;
|
||||
|
@ -416,7 +420,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
|
|||
bool shouldRender = false; // assume we don't need to render it
|
||||
// if it's colored, we might need to render it!
|
||||
shouldRender = node->calculateShouldRender(_viewFrustum);
|
||||
|
||||
|
||||
node->setShouldRender(shouldRender);
|
||||
// let children figure out their renderness
|
||||
if (!node->isLeaf()) {
|
||||
|
@ -627,6 +631,18 @@ void VoxelSystem::init() {
|
|||
_perlinModulateProgram->release();
|
||||
}
|
||||
|
||||
void VoxelSystem::changeTree(VoxelTree* newTree) {
|
||||
disconnect(_tree, 0, this, 0);
|
||||
|
||||
_tree = newTree;
|
||||
_tree->setDirtyBit();
|
||||
|
||||
connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float)));
|
||||
connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int)));
|
||||
|
||||
setupNewVoxelsForDrawing();
|
||||
}
|
||||
|
||||
void VoxelSystem::updateFullVBOs() {
|
||||
updateVBOSegment(0, _voxelsInReadArrays);
|
||||
|
||||
|
|
|
@ -34,19 +34,21 @@ public:
|
|||
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM);
|
||||
~VoxelSystem();
|
||||
|
||||
void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; };
|
||||
int getDataSourceID() const { return _dataSourceID; };
|
||||
void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; }
|
||||
int getDataSourceID() const { return _dataSourceID; }
|
||||
|
||||
int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
virtual void init();
|
||||
void simulate(float deltaTime) { };
|
||||
void simulate(float deltaTime) { }
|
||||
void render(bool texture);
|
||||
|
||||
ViewFrustum* getViewFrustum() const {return _viewFrustum;}
|
||||
void setViewFrustum(ViewFrustum* viewFrustum) {_viewFrustum = viewFrustum;}
|
||||
unsigned long getVoxelsUpdated() const {return _voxelsUpdated;};
|
||||
unsigned long getVoxelsRendered() const {return _voxelsInReadArrays;};
|
||||
void changeTree(VoxelTree* newTree);
|
||||
VoxelTree* getTree() const { return _tree; }
|
||||
ViewFrustum* getViewFrustum() const { return _viewFrustum; }
|
||||
void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; }
|
||||
unsigned long getVoxelsUpdated() const { return _voxelsUpdated; }
|
||||
unsigned long getVoxelsRendered() const { return _voxelsInReadArrays; }
|
||||
|
||||
void loadVoxelsFile(const char* fileName,bool wantColorRandomizer);
|
||||
void writeToSVOFile(const char* filename, VoxelNode* node) const;
|
||||
|
@ -118,10 +120,11 @@ protected:
|
|||
float _treeScale;
|
||||
int _maxVoxels;
|
||||
VoxelTree* _tree;
|
||||
|
||||
void setupNewVoxelsForDrawing();
|
||||
|
||||
glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const;
|
||||
|
||||
void setupNewVoxelsForDrawing();
|
||||
|
||||
|
||||
virtual void updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex,
|
||||
float voxelScale, const nodeColor& color);
|
||||
|
|
|
@ -105,7 +105,8 @@ Avatar::Avatar(Node* owningNode) :
|
|||
_initialized(false),
|
||||
_handHoldingPosition(0.0f, 0.0f, 0.0f),
|
||||
_maxArmLength(0.0f),
|
||||
_pelvisStandingHeight(0.0f)
|
||||
_pelvisStandingHeight(0.0f),
|
||||
_moving(false)
|
||||
{
|
||||
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||
_headData = &_head;
|
||||
|
@ -485,22 +486,27 @@ void Avatar::render(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
// render a simple round on the ground projected down from the avatar's position
|
||||
renderDiskShadow(_position, glm::vec3(0.0f, 1.0f, 0.0f), _scale * 0.1f, 0.2f);
|
||||
|
||||
// render body
|
||||
renderBody(lookingInMirror, renderAvatarBalls);
|
||||
{
|
||||
// glow when moving
|
||||
Glower glower(_moving ? 1.0f : 0.0f);
|
||||
|
||||
// render body
|
||||
renderBody(lookingInMirror, renderAvatarBalls);
|
||||
|
||||
// render sphere when far away
|
||||
const float MAX_ANGLE = 10.f;
|
||||
glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition();
|
||||
glm::vec3 delta = _height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f;
|
||||
float angle = abs(angleBetween(toTarget + delta, toTarget - delta));
|
||||
// render sphere when far away
|
||||
const float MAX_ANGLE = 10.f;
|
||||
glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition();
|
||||
glm::vec3 delta = _height * (_head.getCameraOrientation() * IDENTITY_UP) / 2.f;
|
||||
float angle = abs(angleBetween(toTarget + delta, toTarget - delta));
|
||||
|
||||
if (angle < MAX_ANGLE) {
|
||||
glColor4f(0.5f, 0.8f, 0.8f, 1.f - angle / MAX_ANGLE);
|
||||
glPushMatrix();
|
||||
glTranslatef(_position.x, _position.y, _position.z);
|
||||
glScalef(_height / 2.f, _height / 2.f, _height / 2.f);
|
||||
glutSolidSphere(1.2f + _head.getAverageLoudness() * .0005f, 20, 20);
|
||||
glPopMatrix();
|
||||
if (angle < MAX_ANGLE) {
|
||||
glColor4f(0.5f, 0.8f, 0.8f, 1.f - angle / MAX_ANGLE);
|
||||
glPushMatrix();
|
||||
glTranslatef(_position.x, _position.y, _position.z);
|
||||
glScalef(_height / 2.f, _height / 2.f, _height / 2.f);
|
||||
glutSolidSphere(1.2f + _head.getAverageLoudness() * .0005f, 20, 20);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
// Render the balls
|
||||
|
@ -757,10 +763,11 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// Render the body's voxels
|
||||
// Render the body's voxels and head
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
|
||||
if (alpha > 0.0f) {
|
||||
_voxels.render(false);
|
||||
_head.render(alpha);
|
||||
}
|
||||
}
|
||||
_hand.render(lookingInMirror);
|
||||
|
@ -794,6 +801,30 @@ void Avatar::getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, gl
|
|||
rotation = _bodyBall[jointID].rotation;
|
||||
}
|
||||
|
||||
bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
|
||||
float minDistance = FLT_MAX;
|
||||
for (int i = 0; i < NUM_AVATAR_BODY_BALLS; i++) {
|
||||
float distance;
|
||||
if (rayIntersectsSphere(origin, direction, _bodyBall[i].position, _bodyBall[i].radius, distance)) {
|
||||
minDistance = min(minDistance, distance);
|
||||
}
|
||||
}
|
||||
if (minDistance == FLT_MAX) {
|
||||
return false;
|
||||
}
|
||||
distance = minDistance;
|
||||
return true;
|
||||
}
|
||||
|
||||
int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) {
|
||||
// change in position implies movement
|
||||
glm::vec3 oldPosition = _position;
|
||||
int bytesRead = AvatarData::parseData(sourceBuffer, numBytes);
|
||||
const float MOVE_DISTANCE_THRESHOLD = 0.001f;
|
||||
_moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
void Avatar::saveData(QSettings* set) {
|
||||
set->beginGroup("Avatar");
|
||||
|
||||
|
|
|
@ -164,6 +164,15 @@ public:
|
|||
// Get the position/rotation of a single body ball
|
||||
void getBodyBallTransform(AvatarJointID jointID, glm::vec3& position, glm::quat& rotation) const;
|
||||
|
||||
/// Checks for an intersection between the described ray and any of the avatar's body balls.
|
||||
/// \param origin the origin of the ray
|
||||
/// \param direction the unit direction vector
|
||||
/// \param[out] distance the variable in which to store the distance to intersection
|
||||
/// \return whether or not the ray intersected
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
virtual int parseData(unsigned char* sourceBuffer, int numBytes);
|
||||
|
||||
static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2);
|
||||
|
||||
public slots:
|
||||
|
@ -219,6 +228,8 @@ protected:
|
|||
float _stringLength;
|
||||
AvatarVoxelSystem _voxels;
|
||||
|
||||
bool _moving; ///< set when position is changing
|
||||
|
||||
// protected methods...
|
||||
glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; }
|
||||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
|
|
|
@ -318,6 +318,10 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter, float gyroCam
|
|||
_mode = AVATAR_MODE_INTERACTING;
|
||||
}
|
||||
|
||||
// update moving flag based on speed
|
||||
const float MOVING_SPEED_THRESHOLD = 0.01f;
|
||||
_moving = _speed > MOVING_SPEED_THRESHOLD;
|
||||
|
||||
// update position by velocity, and subtract the change added earlier for gravity
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
|
@ -510,8 +514,16 @@ float MyAvatar::getBallRenderAlpha(int ball, bool lookingInMirror) const {
|
|||
void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
||||
|
||||
if (Application::getInstance()->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
// Dont display body
|
||||
} else if (_head.getFace().isFullFrame()) {
|
||||
// Dont display body, only the hand
|
||||
_hand.render(lookingInMirror);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// glow when moving
|
||||
Glower glower(_moving ? 1.0f : 0.0f);
|
||||
|
||||
if (_head.getFace().isFullFrame()) {
|
||||
// Render the full-frame video
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
|
||||
if (alpha > 0.0f) {
|
||||
|
@ -585,10 +597,11 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// Render the body's voxels
|
||||
// Render the body's voxels and head
|
||||
float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror);
|
||||
if (alpha > 0.0f) {
|
||||
_voxels.render(false);
|
||||
_head.render(alpha);
|
||||
}
|
||||
}
|
||||
_hand.render(lookingInMirror);
|
||||
|
@ -1032,4 +1045,4 @@ void MyAvatar::setOrientation(const glm::quat& orientation) {
|
|||
|
||||
void MyAvatar::setNewScale(const float scale) {
|
||||
_newScale = scale;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "ProgramObject.h"
|
||||
#include "RenderUtil.h"
|
||||
|
||||
GlowEffect::GlowEffect() : _renderMode(DIFFUSE_ADD_MODE), _isOddFrame(false) {
|
||||
GlowEffect::GlowEffect() : _renderMode(DIFFUSE_ADD_MODE), _isOddFrame(false), _intensity(0.0f) {
|
||||
}
|
||||
|
||||
QOpenGLFramebufferObject* GlowEffect::getFreeFramebufferObject() const {
|
||||
|
@ -70,12 +70,15 @@ void GlowEffect::prepare() {
|
|||
}
|
||||
|
||||
void GlowEffect::begin(float intensity) {
|
||||
glBlendColor(0.0f, 0.0f, 0.0f, intensity);
|
||||
_isEmpty = false;
|
||||
// store the current intensity and add the new amount
|
||||
_intensityStack.push(_intensity);
|
||||
glBlendColor(0.0f, 0.0f, 0.0f, _intensity += intensity);
|
||||
_isEmpty &= (_intensity == 0.0f);
|
||||
}
|
||||
|
||||
void GlowEffect::end() {
|
||||
glBlendColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
// restore the saved intensity
|
||||
glBlendColor(0.0f, 0.0f, 0.0f, _intensity = _intensityStack.pop());
|
||||
}
|
||||
|
||||
static void maybeBind(QOpenGLFramebufferObject* fbo) {
|
||||
|
@ -268,3 +271,12 @@ void GlowEffect::cycleRenderMode() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Glower::Glower(float amount) {
|
||||
Application::getInstance()->getGlowEffect()->begin(amount);
|
||||
}
|
||||
|
||||
Glower::~Glower() {
|
||||
Application::getInstance()->getGlowEffect()->end();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#define __interface__GlowEffect__
|
||||
|
||||
#include <QObject>
|
||||
#include <QStack>
|
||||
|
||||
class QOpenGLFramebufferObject;
|
||||
|
||||
|
@ -63,6 +64,17 @@ private:
|
|||
|
||||
bool _isEmpty; ///< set when nothing in the scene is currently glowing
|
||||
bool _isOddFrame; ///< controls the alternation between texture targets in diffuse add mode
|
||||
|
||||
float _intensity;
|
||||
QStack<float> _intensityStack;
|
||||
};
|
||||
|
||||
/// RAII-style glow handler. Applies glow when in scope.
|
||||
class Glower {
|
||||
public:
|
||||
|
||||
Glower(float amount = 1.0f);
|
||||
~Glower();
|
||||
};
|
||||
|
||||
#endif /* defined(__interface__GlowEffect__) */
|
||||
|
|
|
@ -8,9 +8,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm
|
|||
|
||||
set(TARGET_NAME avatars)
|
||||
|
||||
find_package(Qt5Script REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||
setup_hifi_library(${TARGET_NAME})
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Script)
|
||||
|
||||
include(${MACRO_DIR}/IncludeGLM.cmake)
|
||||
include_glm(${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
|
|
|
@ -423,6 +423,7 @@ void ViewFrustum::printDebugDetails() const {
|
|||
qDebug("_right=%f,%f,%f\n", _right.x, _right.y, _right.z );
|
||||
qDebug("_fieldOfView=%f\n", _fieldOfView);
|
||||
qDebug("_aspectRatio=%f\n", _aspectRatio);
|
||||
qDebug("_keyHoleRadius=%f\n", _keyholeRadius);
|
||||
qDebug("_nearClip=%f\n", _nearClip);
|
||||
qDebug("_farClip=%f\n", _farClip);
|
||||
qDebug("_eyeOffsetPosition=%f,%f,%f\n", _eyeOffsetPosition.x, _eyeOffsetPosition.y, _eyeOffsetPosition.z );
|
||||
|
|
|
@ -24,50 +24,50 @@ const float DEFAULT_KEYHOLE_RADIUS = 3.0f;
|
|||
class ViewFrustum {
|
||||
public:
|
||||
// setters for camera attributes
|
||||
void setPosition (const glm::vec3& p) { _position = p; };
|
||||
void setOrientation (const glm::quat& orientationAsQuaternion);
|
||||
void setPosition(const glm::vec3& p) { _position = p; }
|
||||
void setOrientation(const glm::quat& orientationAsQuaternion);
|
||||
|
||||
// getters for camera attributes
|
||||
const glm::vec3& getPosition() const { return _position; };
|
||||
const glm::quat& getOrientation() const { return _orientation; };
|
||||
const glm::vec3& getDirection() const { return _direction; };
|
||||
const glm::vec3& getUp() const { return _up; };
|
||||
const glm::vec3& getRight() const { return _right; };
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::quat& getOrientation() const { return _orientation; }
|
||||
const glm::vec3& getDirection() const { return _direction; }
|
||||
const glm::vec3& getUp() const { return _up; }
|
||||
const glm::vec3& getRight() const { return _right; }
|
||||
|
||||
// setters for lens attributes
|
||||
void setFieldOfView ( float f ) { _fieldOfView = f; };
|
||||
void setAspectRatio ( float a ) { _aspectRatio = a; };
|
||||
void setNearClip ( float n ) { _nearClip = n; };
|
||||
void setFarClip ( float f ) { _farClip = f; };
|
||||
void setEyeOffsetPosition (const glm::vec3& p) { _eyeOffsetPosition = p; };
|
||||
void setEyeOffsetOrientation (const glm::quat& o) { _eyeOffsetOrientation = o; };
|
||||
void setFieldOfView(float f) { _fieldOfView = f; }
|
||||
void setAspectRatio(float a) { _aspectRatio = a; }
|
||||
void setNearClip(float n) { _nearClip = n; }
|
||||
void setFarClip(float f) { _farClip = f; }
|
||||
void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; }
|
||||
void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; }
|
||||
|
||||
// getters for lens attributes
|
||||
float getFieldOfView() const { return _fieldOfView; };
|
||||
float getAspectRatio() const { return _aspectRatio; };
|
||||
float getNearClip() const { return _nearClip; };
|
||||
float getFarClip() const { return _farClip; };
|
||||
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; };
|
||||
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation;};
|
||||
float getFieldOfView() const { return _fieldOfView; }
|
||||
float getAspectRatio() const { return _aspectRatio; }
|
||||
float getNearClip() const { return _nearClip; }
|
||||
float getFarClip() const { return _farClip; }
|
||||
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }
|
||||
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; }
|
||||
|
||||
const glm::vec3& getOffsetPosition() const { return _offsetPosition; };
|
||||
const glm::vec3& getOffsetDirection() const { return _offsetDirection;};
|
||||
const glm::vec3& getOffsetUp() const { return _offsetUp; };
|
||||
const glm::vec3& getOffsetRight() const { return _offsetRight; };
|
||||
const glm::vec3& getOffsetPosition() const { return _offsetPosition; }
|
||||
const glm::vec3& getOffsetDirection() const { return _offsetDirection; }
|
||||
const glm::vec3& getOffsetUp() const { return _offsetUp; }
|
||||
const glm::vec3& getOffsetRight() const { return _offsetRight; }
|
||||
|
||||
const glm::vec3& getFarTopLeft() const { return _farTopLeft; };
|
||||
const glm::vec3& getFarTopRight() const { return _farTopRight; };
|
||||
const glm::vec3& getFarBottomLeft() const { return _farBottomLeft; };
|
||||
const glm::vec3& getFarBottomRight() const { return _farBottomRight; };
|
||||
const glm::vec3& getFarTopLeft() const { return _farTopLeft; }
|
||||
const glm::vec3& getFarTopRight() const { return _farTopRight; }
|
||||
const glm::vec3& getFarBottomLeft() const { return _farBottomLeft; }
|
||||
const glm::vec3& getFarBottomRight() const { return _farBottomRight; }
|
||||
|
||||
const glm::vec3& getNearTopLeft() const { return _nearTopLeft; };
|
||||
const glm::vec3& getNearTopRight() const { return _nearTopRight; };
|
||||
const glm::vec3& getNearBottomLeft() const { return _nearBottomLeft; };
|
||||
const glm::vec3& getNearBottomRight() const { return _nearBottomRight;};
|
||||
const glm::vec3& getNearTopLeft() const { return _nearTopLeft; }
|
||||
const glm::vec3& getNearTopRight() const { return _nearTopRight; }
|
||||
const glm::vec3& getNearBottomLeft() const { return _nearBottomLeft; }
|
||||
const glm::vec3& getNearBottomRight() const { return _nearBottomRight; }
|
||||
|
||||
// get/set for keyhole attribute
|
||||
void setKeyholeRadius(float keyholdRadius) { _keyholeRadius = keyholdRadius; };
|
||||
float getKeyholeRadius() const { return _keyholeRadius; };
|
||||
void setKeyholeRadius(float keyholdRadius) { _keyholeRadius = keyholdRadius; }
|
||||
float getKeyholeRadius() const { return _keyholeRadius; }
|
||||
|
||||
void calculate();
|
||||
|
||||
|
@ -81,7 +81,7 @@ public:
|
|||
|
||||
// some frustum comparisons
|
||||
bool matches(const ViewFrustum& compareTo, bool debug = false) const;
|
||||
bool matches(const ViewFrustum* compareTo, bool debug = false) const { return matches(*compareTo, debug); };
|
||||
bool matches(const ViewFrustum* compareTo, bool debug = false) const { return matches(*compareTo, debug); }
|
||||
|
||||
void computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const;
|
||||
|
||||
|
|
|
@ -1581,7 +1581,6 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp
|
|||
bool VoxelTree::readFromSVOFile(const char* fileName) {
|
||||
std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate);
|
||||
if(file.is_open()) {
|
||||
|
||||
emit importSize(1.0f, 1.0f, 1.0f);
|
||||
emit importProgress(0);
|
||||
|
||||
|
@ -1607,15 +1606,15 @@ bool VoxelTree::readFromSVOFile(const char* fileName) {
|
|||
}
|
||||
|
||||
bool VoxelTree::readFromSquareARGB32Pixels(const char* filename) {
|
||||
emit importSize(1.0f, 1.0f, 1.0f);
|
||||
emit importProgress(0);
|
||||
|
||||
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);
|
||||
for (int i = 0; i < pngImage.width(); ++i) {
|
||||
for (int j = 0; j < pngImage.height(); ++j) {
|
||||
minAlpha = std::min(qAlpha(pngImage.pixel(i, j)) , minAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
int maxSize = std::max(pngImage.width(), pngImage.height());
|
||||
|
@ -1624,6 +1623,8 @@ bool VoxelTree::readFromSquareARGB32Pixels(const char* filename) {
|
|||
while (maxSize > scale) {scale *= 2;}
|
||||
float size = 1.0f / scale;
|
||||
|
||||
emit importSize(size * pngImage.width(), 1.0f, size * pngImage.height());
|
||||
|
||||
QRgb pixel;
|
||||
int minNeighborhoodAlpha;
|
||||
|
||||
|
@ -1692,14 +1693,13 @@ bool VoxelTree::readFromSchematicFile(const char *fileName) {
|
|||
while (max > scale) {scale *= 2;}
|
||||
float size = 1.0f / scale;
|
||||
|
||||
int create = 1;
|
||||
int red = 128, green = 128, blue = 128;
|
||||
int count = 0;
|
||||
|
||||
emit importSize(size * schematics.getWidth(),
|
||||
size * schematics.getHeight(),
|
||||
size * schematics.getLength());
|
||||
emit importProgress(0);
|
||||
|
||||
int create = 1;
|
||||
int red = 128, green = 128, blue = 128;
|
||||
int count = 0;
|
||||
|
||||
for (int y = 0; y < schematics.getHeight(); ++y) {
|
||||
for (int z = 0; z < schematics.getLength(); ++z) {
|
||||
|
|
26
voxel-server/src/NodeWatcher.cpp
Normal file
26
voxel-server/src/NodeWatcher.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// NodeWatcher.h
|
||||
// voxel-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Node List Hook that watches for Node's being killed in order to clean up node specific memory and threads
|
||||
//
|
||||
|
||||
#include <NodeList.h>
|
||||
#include "NodeWatcher.h"
|
||||
#include "VoxelNodeData.h"
|
||||
|
||||
void NodeWatcher::nodeAdded(Node* node) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void NodeWatcher::nodeKilled(Node* node) {
|
||||
// Use this to cleanup our node
|
||||
if (node->getType() == NODE_TYPE_AGENT) {
|
||||
VoxelNodeData* nodeData = (VoxelNodeData*)node->getLinkedData();
|
||||
node->setLinkedData(NULL);
|
||||
delete nodeData;
|
||||
}
|
||||
};
|
23
voxel-server/src/NodeWatcher.h
Normal file
23
voxel-server/src/NodeWatcher.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// NodeWatcher.h
|
||||
// voxel-server
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Node List Hook that watches for Node's being killed in order to clean up node specific memory and threads
|
||||
//
|
||||
|
||||
#ifndef __voxel_server__NodeWatcher__
|
||||
#define __voxel_server__NodeWatcher__
|
||||
|
||||
#include <NodeList.h>
|
||||
|
||||
/// Voxel server's node watcher, which watches for nodes being killed and cleans up their data and threads
|
||||
class NodeWatcher : public virtual NodeListHook {
|
||||
public:
|
||||
virtual void nodeAdded(Node* node);
|
||||
virtual void nodeKilled(Node* node);
|
||||
};
|
||||
|
||||
#endif // __voxel_server__NodeWatcher__
|
|
@ -11,6 +11,7 @@
|
|||
#include "VoxelNodeData.h"
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include "VoxelSendThread.h"
|
||||
|
||||
VoxelNodeData::VoxelNodeData(Node* owningNode) :
|
||||
AvatarData(owningNode),
|
||||
|
@ -21,11 +22,17 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
|
|||
_lastTimeBagEmpty(0),
|
||||
_viewFrustumChanging(false),
|
||||
_viewFrustumJustStoppedChanging(true),
|
||||
_currentPacketIsColor(true)
|
||||
_currentPacketIsColor(true),
|
||||
_voxelSendThread(NULL)
|
||||
{
|
||||
_voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE];
|
||||
_voxelPacketAt = _voxelPacket;
|
||||
resetVoxelPacket();
|
||||
|
||||
// Create voxel sending thread...
|
||||
uint16_t nodeID = getOwningNode()->getNodeID();
|
||||
_voxelSendThread = new VoxelSendThread(nodeID);
|
||||
_voxelSendThread->initialize(true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -49,6 +56,9 @@ void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) {
|
|||
|
||||
VoxelNodeData::~VoxelNodeData() {
|
||||
delete[] _voxelPacket;
|
||||
|
||||
_voxelSendThread->terminate();
|
||||
delete _voxelSendThread;
|
||||
}
|
||||
|
||||
bool VoxelNodeData::updateCurrentViewFrustum() {
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <VoxelNodeBag.h>
|
||||
#include <VoxelSceneStats.h>
|
||||
|
||||
class VoxelSendThread;
|
||||
|
||||
class VoxelNodeData : public AvatarData {
|
||||
public:
|
||||
VoxelNodeData(Node* owningNode);
|
||||
|
@ -80,6 +82,8 @@ private:
|
|||
bool _viewFrustumChanging;
|
||||
bool _viewFrustumJustStoppedChanging;
|
||||
bool _currentPacketIsColor;
|
||||
|
||||
VoxelSendThread* _voxelSendThread;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__VoxelNodeData__) */
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded voxel persistance
|
||||
// Threaded or non-threaded voxel persistence
|
||||
//
|
||||
|
||||
#include <NodeList.h>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Created by Brad Hefta-Gaub on 8/21/13
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Threaded or non-threaded voxel persistance
|
||||
// Threaded or non-threaded voxel persistence
|
||||
//
|
||||
|
||||
#ifndef __voxel_server__VoxelPersistThread__
|
||||
|
|
303
voxel-server/src/VoxelSendThread.cpp
Normal file
303
voxel-server/src/VoxelSendThread.cpp
Normal file
|
@ -0,0 +1,303 @@
|
|||
//
|
||||
// VoxelSendThread.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 packet sender
|
||||
//
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
#include "VoxelSendThread.h"
|
||||
#include "VoxelServer.h"
|
||||
|
||||
VoxelSendThread::VoxelSendThread(uint16_t nodeID) :
|
||||
_nodeID(nodeID) {
|
||||
}
|
||||
|
||||
bool VoxelSendThread::process() {
|
||||
uint64_t lastSendTime = usecTimestampNow();
|
||||
|
||||
Node* node = NodeList::getInstance()->nodeWithID(_nodeID);
|
||||
VoxelNodeData* nodeData = NULL;
|
||||
|
||||
if (node) {
|
||||
nodeData = (VoxelNodeData*) node->getLinkedData();
|
||||
}
|
||||
|
||||
// Sometimes the node data has not yet been linked, in which case we can't really do anything
|
||||
if (nodeData) {
|
||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||
if (::debugVoxelSending) {
|
||||
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
|
||||
}
|
||||
deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
|
||||
// dynamically sleep until we need to fire off the next set of voxels
|
||||
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - lastSendTime);
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
} else {
|
||||
if (::debugVoxelSending) {
|
||||
std::cout << "Last send took too much time, not sleeping!\n";
|
||||
}
|
||||
}
|
||||
|
||||
return isStillRunning(); // keep running till they terminate us
|
||||
}
|
||||
|
||||
|
||||
void VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent) {
|
||||
|
||||
// If we've got a stats message ready to send, then see if we can piggyback them together
|
||||
if (nodeData->stats.isReadyToSend()) {
|
||||
// Send the stats message to the client
|
||||
unsigned char* statsMessage = nodeData->stats.getStatsMessage();
|
||||
int statsMessageLength = nodeData->stats.getStatsMessageLength();
|
||||
|
||||
// If the size of the stats message and the voxel message will fit in a packet, then piggyback them
|
||||
if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) {
|
||||
|
||||
// copy voxel message to back of stats message
|
||||
memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength());
|
||||
statsMessageLength += nodeData->getPacketLength();
|
||||
|
||||
// actually send it
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
|
||||
} else {
|
||||
// not enough room in the packet, send two packets
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
}
|
||||
} else {
|
||||
// just send the voxel packet
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
}
|
||||
// remember to track our stats
|
||||
nodeData->stats.packetSent(nodeData->getPacketLength());
|
||||
trueBytesSent += nodeData->getPacketLength();
|
||||
truePacketsSent++;
|
||||
nodeData->resetVoxelPacket();
|
||||
}
|
||||
|
||||
/// Version of voxel distributor that sends the deepest LOD level at once
|
||||
void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) {
|
||||
|
||||
pthread_mutex_lock(&::treeLock);
|
||||
|
||||
int truePacketsSent = 0;
|
||||
int trueBytesSent = 0;
|
||||
|
||||
// FOR NOW... node tells us if it wants to receive only view frustum deltas
|
||||
bool wantDelta = viewFrustumChanged && nodeData->getWantDelta();
|
||||
|
||||
// If our packet already has content in it, then we must use the color choice of the waiting packet.
|
||||
// If we're starting a fresh packet, then...
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor();
|
||||
|
||||
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
|
||||
// then let's just send that waiting packet.
|
||||
if (wantColor != nodeData->getCurrentPacketIsColor()) {
|
||||
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
if (::debugVoxelSending) {
|
||||
printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
|
||||
}
|
||||
|
||||
handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
|
||||
} else {
|
||||
if (::debugVoxelSending) {
|
||||
printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
|
||||
}
|
||||
nodeData->resetVoxelPacket();
|
||||
}
|
||||
}
|
||||
|
||||
if (::debugVoxelSending) {
|
||||
printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving()));
|
||||
}
|
||||
|
||||
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
|
||||
|
||||
if (::debugVoxelSending) {
|
||||
printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()),
|
||||
debug::valueOf(nodeData->getViewSent())
|
||||
);
|
||||
}
|
||||
|
||||
// If the current view frustum has changed OR we have nothing to send, then search against
|
||||
// the current view frustum for things to send.
|
||||
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
if (::debugVoxelSending) {
|
||||
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
|
||||
if (nodeData->getLastTimeBagEmpty() > 0) {
|
||||
float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f;
|
||||
if (viewFrustumChanged) {
|
||||
printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend);
|
||||
} else {
|
||||
printf("elapsed time to send scene = %f seconds", elapsedSceneSend);
|
||||
}
|
||||
printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n",
|
||||
debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta),
|
||||
debug::valueOf(wantColor));
|
||||
}
|
||||
}
|
||||
|
||||
// if our view has changed, we need to reset these things...
|
||||
if (viewFrustumChanged) {
|
||||
if (::dumpVoxelsOnMove) {
|
||||
nodeData->nodeBag.deleteAll();
|
||||
}
|
||||
nodeData->map.erase();
|
||||
}
|
||||
|
||||
if (!viewFrustumChanged && !nodeData->getWantDelta()) {
|
||||
// only set our last sent time if we weren't resetting due to frustum change
|
||||
uint64_t now = usecTimestampNow();
|
||||
nodeData->setLastTimeBagEmpty(now);
|
||||
}
|
||||
|
||||
nodeData->stats.sceneCompleted();
|
||||
|
||||
if (::displayVoxelStats) {
|
||||
nodeData->stats.printDebugDetails();
|
||||
}
|
||||
|
||||
// start tracking our stats
|
||||
bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging();
|
||||
|
||||
// If we're starting a full scene, then definitely we want to empty the nodeBag
|
||||
if (isFullScene) {
|
||||
nodeData->nodeBag.deleteAll();
|
||||
}
|
||||
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction);
|
||||
|
||||
// This is the start of "resending" the scene.
|
||||
nodeData->nodeBag.insert(serverTree.rootNode);
|
||||
}
|
||||
|
||||
// If we have something in our nodeBag, then turn them into packets and send them out...
|
||||
if (!nodeData->nodeBag.isEmpty()) {
|
||||
int bytesWritten = 0;
|
||||
int packetsSentThisInterval = 0;
|
||||
uint64_t start = usecTimestampNow();
|
||||
|
||||
bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
|
||||
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
|
||||
// Check to see if we're taking too long, and if so bail early...
|
||||
uint64_t now = usecTimestampNow();
|
||||
long elapsedUsec = (now - start);
|
||||
long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent);
|
||||
long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec);
|
||||
|
||||
if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) {
|
||||
if (::debugVoxelSending) {
|
||||
printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n",
|
||||
usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket,
|
||||
nodeData->nodeBag.count());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!nodeData->nodeBag.isEmpty()) {
|
||||
VoxelNode* subTree = nodeData->nodeBag.extract();
|
||||
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
|
||||
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
|
||||
int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving()
|
||||
? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST;
|
||||
|
||||
bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) &&
|
||||
nodeData->getViewFrustumJustStoppedChanging();
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
|
||||
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
||||
wantOcclusionCulling, coverageMap, boundaryLevelAdjust,
|
||||
nodeData->getLastTimeBagEmpty(),
|
||||
isFullScene, &nodeData->stats, ::jurisdiction);
|
||||
|
||||
nodeData->stats.encodeStarted();
|
||||
bytesWritten = serverTree.encodeTreeBitstream(subTree, _tempOutputBuffer, MAX_VOXEL_PACKET_SIZE - 1,
|
||||
nodeData->nodeBag, params);
|
||||
nodeData->stats.encodeStopped();
|
||||
|
||||
if (nodeData->getAvailable() >= bytesWritten) {
|
||||
nodeData->writeToPacket(_tempOutputBuffer, bytesWritten);
|
||||
} else {
|
||||
handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
packetsSentThisInterval++;
|
||||
nodeData->resetVoxelPacket();
|
||||
nodeData->writeToPacket(_tempOutputBuffer, bytesWritten);
|
||||
}
|
||||
} else {
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
|
||||
nodeData->resetVoxelPacket();
|
||||
}
|
||||
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left
|
||||
}
|
||||
}
|
||||
// send the environment packet
|
||||
if (shouldSendEnvironments) {
|
||||
int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
|
||||
int envPacketLength = numBytesPacketHeader;
|
||||
int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData);
|
||||
|
||||
for (int i = 0; i < environmentsToSend; i++) {
|
||||
envPacketLength += environmentData[i].getBroadcastData(_tempOutputBuffer + envPacketLength);
|
||||
}
|
||||
|
||||
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), _tempOutputBuffer, envPacketLength);
|
||||
trueBytesSent += envPacketLength;
|
||||
truePacketsSent++;
|
||||
}
|
||||
|
||||
uint64_t end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start)/1000;
|
||||
if (elapsedmsec > 100) {
|
||||
if (elapsedmsec > 1000) {
|
||||
int elapsedsec = (end - start)/1000000;
|
||||
printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n",
|
||||
elapsedsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
|
||||
} else {
|
||||
printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
|
||||
elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
|
||||
}
|
||||
} else if (::debugVoxelSending) {
|
||||
printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
|
||||
elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
|
||||
}
|
||||
|
||||
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
|
||||
// the voxels from the current view frustum
|
||||
if (nodeData->nodeBag.isEmpty()) {
|
||||
nodeData->updateLastKnownViewFrustum();
|
||||
nodeData->setViewSent(true);
|
||||
if (::debugVoxelSending) {
|
||||
nodeData->map.printStats();
|
||||
}
|
||||
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
|
||||
}
|
||||
|
||||
} // end if bag wasn't empty, and so we sent stuff...
|
||||
|
||||
pthread_mutex_unlock(&::treeLock);
|
||||
}
|
||||
|
37
voxel-server/src/VoxelSendThread.h
Normal file
37
voxel-server/src/VoxelSendThread.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// VoxelSendThread.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 object for sending voxels to a client
|
||||
//
|
||||
|
||||
#ifndef __voxel_server__VoxelSendThread__
|
||||
#define __voxel_server__VoxelSendThread__
|
||||
|
||||
#include <GenericThread.h>
|
||||
#include <NetworkPacket.h>
|
||||
#include <VoxelTree.h>
|
||||
#include <VoxelNodeBag.h>
|
||||
#include "VoxelNodeData.h"
|
||||
|
||||
/// Threaded processor for sending voxel packets to a single client
|
||||
class VoxelSendThread : public virtual GenericThread {
|
||||
public:
|
||||
VoxelSendThread(uint16_t nodeID);
|
||||
protected:
|
||||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process();
|
||||
|
||||
private:
|
||||
uint16_t _nodeID;
|
||||
|
||||
void handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent);
|
||||
void deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged);
|
||||
|
||||
unsigned char _tempOutputBuffer[MAX_VOXEL_PACKET_SIZE];
|
||||
};
|
||||
|
||||
#endif // __voxel_server__VoxelSendThread__
|
|
@ -23,7 +23,9 @@
|
|||
#include <PerfStat.h>
|
||||
#include <JurisdictionSender.h>
|
||||
|
||||
#include "NodeWatcher.h"
|
||||
#include "VoxelPersistThread.h"
|
||||
#include "VoxelSendThread.h"
|
||||
#include "VoxelServerPacketProcessor.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -58,297 +60,7 @@ JurisdictionSender* jurisdictionSender = NULL;
|
|||
VoxelServerPacketProcessor* voxelServerPacketProcessor = NULL;
|
||||
VoxelPersistThread* voxelPersistThread = NULL;
|
||||
pthread_mutex_t treeLock;
|
||||
|
||||
|
||||
void handlePacketSend(NodeList* nodeList,
|
||||
NodeList::iterator& node,
|
||||
VoxelNodeData* nodeData,
|
||||
int& trueBytesSent, int& truePacketsSent) {
|
||||
// If we've got a stats message ready to send, then see if we can piggyback them together
|
||||
if (nodeData->stats.isReadyToSend()) {
|
||||
// Send the stats message to the client
|
||||
unsigned char* statsMessage = nodeData->stats.getStatsMessage();
|
||||
int statsMessageLength = nodeData->stats.getStatsMessageLength();
|
||||
|
||||
// If the size of the stats message and the voxel message will fit in a packet, then piggyback them
|
||||
if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) {
|
||||
|
||||
// copy voxel message to back of stats message
|
||||
memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength());
|
||||
statsMessageLength += nodeData->getPacketLength();
|
||||
|
||||
// actually send it
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
|
||||
} else {
|
||||
// not enough room in the packet, send two packets
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
}
|
||||
} else {
|
||||
// just send the voxel packet
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(),
|
||||
nodeData->getPacket(), nodeData->getPacketLength());
|
||||
}
|
||||
// remember to track our stats
|
||||
nodeData->stats.packetSent(nodeData->getPacketLength());
|
||||
trueBytesSent += nodeData->getPacketLength();
|
||||
truePacketsSent++;
|
||||
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;
|
||||
int trueBytesSent = 0;
|
||||
|
||||
// FOR NOW... node tells us if it wants to receive only view frustum deltas
|
||||
bool wantDelta = viewFrustumChanged && nodeData->getWantDelta();
|
||||
|
||||
// If our packet already has content in it, then we must use the color choice of the waiting packet.
|
||||
// If we're starting a fresh packet, then...
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
bool wantColor = LOW_RES_MONO && nodeData->getWantLowResMoving() && viewFrustumChanged ? false : nodeData->getWantColor();
|
||||
|
||||
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
|
||||
// then let's just send that waiting packet.
|
||||
if (wantColor != nodeData->getCurrentPacketIsColor()) {
|
||||
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
if (::debugVoxelSending) {
|
||||
printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
|
||||
}
|
||||
|
||||
handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent);
|
||||
|
||||
} else {
|
||||
if (::debugVoxelSending) {
|
||||
printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
|
||||
}
|
||||
nodeData->resetVoxelPacket();
|
||||
}
|
||||
}
|
||||
|
||||
if (::debugVoxelSending) {
|
||||
printf("wantColor=%s getCurrentPacketIsColor()=%s, viewFrustumChanged=%s, getWantLowResMoving()=%s\n",
|
||||
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving()));
|
||||
}
|
||||
|
||||
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
|
||||
|
||||
if (::debugVoxelSending) {
|
||||
printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()),
|
||||
debug::valueOf(nodeData->getViewSent())
|
||||
);
|
||||
}
|
||||
|
||||
// If the current view frustum has changed OR we have nothing to send, then search against
|
||||
// the current view frustum for things to send.
|
||||
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
if (::debugVoxelSending) {
|
||||
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
|
||||
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
|
||||
if (nodeData->getLastTimeBagEmpty() > 0) {
|
||||
float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f;
|
||||
if (viewFrustumChanged) {
|
||||
printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend);
|
||||
} else {
|
||||
printf("elapsed time to send scene = %f seconds", elapsedSceneSend);
|
||||
}
|
||||
printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n",
|
||||
debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta),
|
||||
debug::valueOf(wantColor));
|
||||
}
|
||||
}
|
||||
|
||||
// if our view has changed, we need to reset these things...
|
||||
if (viewFrustumChanged) {
|
||||
if (::dumpVoxelsOnMove) {
|
||||
nodeData->nodeBag.deleteAll();
|
||||
}
|
||||
nodeData->map.erase();
|
||||
}
|
||||
|
||||
if (!viewFrustumChanged && !nodeData->getWantDelta()) {
|
||||
// only set our last sent time if we weren't resetting due to frustum change
|
||||
uint64_t now = usecTimestampNow();
|
||||
nodeData->setLastTimeBagEmpty(now);
|
||||
}
|
||||
|
||||
nodeData->stats.sceneCompleted();
|
||||
|
||||
if (::displayVoxelStats) {
|
||||
nodeData->stats.printDebugDetails();
|
||||
}
|
||||
|
||||
// start tracking our stats
|
||||
bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging();
|
||||
|
||||
// If we're starting a full scene, then definitely we want to empty the nodeBag
|
||||
if (isFullScene) {
|
||||
nodeData->nodeBag.deleteAll();
|
||||
}
|
||||
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, ::serverTree.rootNode, ::jurisdiction);
|
||||
|
||||
// This is the start of "resending" the scene.
|
||||
nodeData->nodeBag.insert(serverTree.rootNode);
|
||||
}
|
||||
|
||||
// If we have something in our nodeBag, then turn them into packets and send them out...
|
||||
if (!nodeData->nodeBag.isEmpty()) {
|
||||
static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
|
||||
int bytesWritten = 0;
|
||||
int packetsSentThisInterval = 0;
|
||||
uint64_t start = usecTimestampNow();
|
||||
|
||||
bool shouldSendEnvironments = ::sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
|
||||
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - (shouldSendEnvironments ? 1 : 0)) {
|
||||
// Check to see if we're taking too long, and if so bail early...
|
||||
uint64_t now = usecTimestampNow();
|
||||
long elapsedUsec = (now - start);
|
||||
long elapsedUsecPerPacket = (truePacketsSent == 0) ? 0 : (elapsedUsec / truePacketsSent);
|
||||
long usecRemaining = (VOXEL_SEND_INTERVAL_USECS - elapsedUsec);
|
||||
|
||||
if (elapsedUsecPerPacket + SENDING_TIME_TO_SPARE > usecRemaining) {
|
||||
if (::debugVoxelSending) {
|
||||
printf("packetLoop() usecRemaining=%ld bailing early took %ld usecs to generate %d bytes in %d packets (%ld usec avg), %d nodes still to send\n",
|
||||
usecRemaining, elapsedUsec, trueBytesSent, truePacketsSent, elapsedUsecPerPacket,
|
||||
nodeData->nodeBag.count());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!nodeData->nodeBag.isEmpty()) {
|
||||
VoxelNode* subTree = nodeData->nodeBag.extract();
|
||||
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
|
||||
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
|
||||
int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving()
|
||||
? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST;
|
||||
|
||||
bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) &&
|
||||
nodeData->getViewFrustumJustStoppedChanging();
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
|
||||
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
||||
wantOcclusionCulling, coverageMap, boundaryLevelAdjust,
|
||||
nodeData->getLastTimeBagEmpty(),
|
||||
isFullScene, &nodeData->stats, ::jurisdiction);
|
||||
|
||||
nodeData->stats.encodeStarted();
|
||||
bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
|
||||
nodeData->nodeBag, params);
|
||||
nodeData->stats.encodeStopped();
|
||||
|
||||
if (nodeData->getAvailable() >= bytesWritten) {
|
||||
nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
|
||||
} else {
|
||||
handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent);
|
||||
packetsSentThisInterval++;
|
||||
nodeData->resetVoxelPacket();
|
||||
nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
|
||||
}
|
||||
} else {
|
||||
if (nodeData->isPacketWaiting()) {
|
||||
handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent);
|
||||
nodeData->resetVoxelPacket();
|
||||
}
|
||||
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left
|
||||
}
|
||||
}
|
||||
// send the environment packet
|
||||
if (shouldSendEnvironments) {
|
||||
int numBytesPacketHeader = populateTypeAndVersion(tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
|
||||
int envPacketLength = numBytesPacketHeader;
|
||||
int environmentsToSend = ::sendMinimalEnvironment ? 1 : sizeof(environmentData) / sizeof(EnvironmentData);
|
||||
|
||||
for (int i = 0; i < environmentsToSend; i++) {
|
||||
envPacketLength += environmentData[i].getBroadcastData(tempOutputBuffer + envPacketLength);
|
||||
}
|
||||
|
||||
nodeList->getNodeSocket()->send(node->getActiveSocket(), tempOutputBuffer, envPacketLength);
|
||||
trueBytesSent += envPacketLength;
|
||||
truePacketsSent++;
|
||||
}
|
||||
|
||||
uint64_t end = usecTimestampNow();
|
||||
int elapsedmsec = (end - start)/1000;
|
||||
if (elapsedmsec > 100) {
|
||||
if (elapsedmsec > 1000) {
|
||||
int elapsedsec = (end - start)/1000000;
|
||||
printf("WARNING! packetLoop() took %d seconds to generate %d bytes in %d packets %d nodes still to send\n",
|
||||
elapsedsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
|
||||
} else {
|
||||
printf("WARNING! packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
|
||||
elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
|
||||
}
|
||||
} else if (::debugVoxelSending) {
|
||||
printf("packetLoop() took %d milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
|
||||
elapsedmsec, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
|
||||
}
|
||||
|
||||
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
|
||||
// the voxels from the current view frustum
|
||||
if (nodeData->nodeBag.isEmpty()) {
|
||||
nodeData->updateLastKnownViewFrustum();
|
||||
nodeData->setViewSent(true);
|
||||
if (::debugVoxelSending) {
|
||||
nodeData->map.printStats();
|
||||
}
|
||||
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
|
||||
}
|
||||
|
||||
} // end if bag wasn't empty, and so we sent stuff...
|
||||
|
||||
pthread_mutex_unlock(&::treeLock);
|
||||
}
|
||||
|
||||
void* distributeVoxelsToListeners(void* args) {
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
timeval lastSendTime;
|
||||
|
||||
while (true) {
|
||||
gettimeofday(&lastSendTime, NULL);
|
||||
|
||||
for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) {
|
||||
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
|
||||
|
||||
// Sometimes the node data has not yet been linked, in which case we can't really do anything
|
||||
if (nodeData) {
|
||||
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
|
||||
if (::debugVoxelSending) {
|
||||
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
|
||||
}
|
||||
deepestLevelVoxelDistributor(nodeList, node, nodeData, viewFrustumChanged);
|
||||
}
|
||||
}
|
||||
|
||||
// dynamically sleep until we need to fire off the next set of voxels
|
||||
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
|
||||
|
||||
if (usecToSleep > 0) {
|
||||
usleep(usecToSleep);
|
||||
} else {
|
||||
if (::debugVoxelSending) {
|
||||
std::cout << "Last send took too much time, not sleeping!\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_exit(0);
|
||||
}
|
||||
NodeWatcher nodeWatcher; // used to cleanup AGENT data when agents are killed
|
||||
|
||||
void attachVoxelNodeDataToNode(Node* newNode) {
|
||||
if (newNode->getLinkedData() == NULL) {
|
||||
|
@ -420,6 +132,9 @@ int main(int argc, const char * argv[]) {
|
|||
|
||||
NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, listenPort);
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
// tell our NodeList about our desire to get notifications
|
||||
nodeList->addHook(&nodeWatcher);
|
||||
|
||||
// Handle Local Domain testing with the --local command line
|
||||
const char* local = "--local";
|
||||
|
@ -462,7 +177,7 @@ int main(int argc, const char * argv[]) {
|
|||
}
|
||||
printf("wantVoxelPersist=%s\n", debug::valueOf(::wantVoxelPersist));
|
||||
|
||||
// if we want Voxel Persistance, load the local file now...
|
||||
// if we want Voxel Persistence, load the local file now...
|
||||
bool persistantFileRead = false;
|
||||
if (::wantVoxelPersist) {
|
||||
|
||||
|
@ -532,9 +247,6 @@ int main(int argc, const char * argv[]) {
|
|||
environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE);
|
||||
environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f);
|
||||
environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue
|
||||
|
||||
pthread_t sendVoxelThread;
|
||||
pthread_create(&sendVoxelThread, NULL, distributeVoxelsToListeners, NULL);
|
||||
|
||||
sockaddr senderAddress;
|
||||
|
||||
|
@ -597,9 +309,6 @@ int main(int argc, const char * argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
pthread_join(sendVoxelThread, NULL);
|
||||
pthread_mutex_destroy(&::treeLock);
|
||||
|
||||
if (::jurisdiction) {
|
||||
delete ::jurisdiction;
|
||||
}
|
||||
|
@ -618,6 +327,11 @@ int main(int argc, const char * argv[]) {
|
|||
::voxelPersistThread->terminate();
|
||||
delete ::voxelPersistThread;
|
||||
}
|
||||
|
||||
// tell our NodeList we're done with notifications
|
||||
nodeList->removeHook(&nodeWatcher);
|
||||
|
||||
pthread_mutex_destroy(&::treeLock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue