diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2deec90108..0763e8d0ab 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -450,7 +451,7 @@ void Application::resizeGL(int width, int height) { } } else { camera.setAspectRatio(aspectRatio); - camera.setFieldOfView(fov = 60); + camera.setFieldOfView(fov = _horizontalFieldOfView); } // Tell our viewFrustum about this change @@ -1046,6 +1047,12 @@ void Application::editPreferences() { avatarURL->setMinimumWidth(400); form->addRow("Avatar URL:", avatarURL); + QSpinBox* horizontalFieldOfView = new QSpinBox(); + horizontalFieldOfView->setMaximum(180); + horizontalFieldOfView->setMinimum(1); + horizontalFieldOfView->setValue(_horizontalFieldOfView); + form->addRow("Horizontal field of view (degrees):", horizontalFieldOfView); + QDoubleSpinBox* headCameraPitchYawScale = new QDoubleSpinBox(); headCameraPitchYawScale->setValue(_headCameraPitchYawScale); form->addRow("Head Camera Pitch/Yaw Scale:", headCameraPitchYawScale); @@ -1077,6 +1084,9 @@ void Application::editPreferences() { if (!shouldDynamicallySetJitterBuffer()) { _audio.setJitterBufferSamples(_audioJitterBufferSamples); } + _horizontalFieldOfView = horizontalFieldOfView->value(); + resizeGL(_glWidget->width(), _glWidget->height()); + } void Application::pair() { @@ -1270,6 +1280,12 @@ bool Application::sendVoxelsOperation(VoxelNode* node, void* extraData) { codeColorBuffer[bytesInCode + RED_INDEX ] = node->getColor()[RED_INDEX ]; codeColorBuffer[bytesInCode + GREEN_INDEX] = node->getColor()[GREEN_INDEX]; codeColorBuffer[bytesInCode + BLUE_INDEX ] = node->getColor()[BLUE_INDEX ]; + + // TODO: sendVoxelsOperation() is sending voxels too fast. + // This printf function accidently slowed down sending + // and hot-fixed the bug when importing + // large PNG models (256x256 px and more) + static unsigned int sendVoxelsOperationCalled = 0; printf("sending voxel #%u\n", ++sendVoxelsOperationCalled); // if we have room don't have room in the buffer, then send the previously generated message first if (args->bufferInUse + codeAndColorLength > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) { @@ -1306,14 +1322,31 @@ void Application::exportVoxels() { void Application::importVoxels() { QString desktopLocation = QDesktopServices::storageLocation(QDesktopServices::DesktopLocation); QString fileNameString = QFileDialog::getOpenFileName(_glWidget, tr("Import Voxels"), desktopLocation, - tr("Sparse Voxel Octree Files (*.svo)")); + tr("Sparse Voxel Octree Files, Square PNG (*.svo *.png)")); QByteArray fileNameAscii = fileNameString.toAscii(); const char* fileName = fileNameAscii.data(); - - // Read the file into a tree + VoxelTree importVoxels; - importVoxels.readFromSVOFile(fileName); - + if (fileNameString.endsWith(".png", Qt::CaseInsensitive)) { + QImage pngImage = QImage(fileName); + if (pngImage.height() != pngImage.width()) { + printLog("ERROR: Bad PNG size: height != width.\n"); + return; + } + + const uint32_t* pixels; + if (pngImage.format() == QImage::Format_ARGB32) { + pixels = reinterpret_cast(pngImage.constBits()); + } else { + QImage tmp = pngImage.convertToFormat(QImage::Format_ARGB32); + pixels = reinterpret_cast(tmp.constBits()); + } + + importVoxels.readFromSquareARGB32Pixels(pixels, pngImage.height()); + } else { + importVoxels.readFromSVOFile(fileName); + } + VoxelNode* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); // Recurse the Import Voxels tree, where everything is root relative, and send all the colored voxels to @@ -1601,7 +1634,7 @@ void Application::init() { _myAvatar.init(); _myAvatar.setPosition(START_LOCATION); - _myCamera.setMode(CAMERA_MODE_THIRD_PERSON); + _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setModeShiftRate(1.0f); _myAvatar.setDisplayingLookatVectors(false); @@ -1659,7 +1692,7 @@ void Application::update(float deltaTime) { _myAvatar.getHead().setLookAtPosition(myLookAtFromMouse); // If we are dragging on a voxel, add thrust according to the amount the mouse is dragging - const float VOXEL_GRAB_THRUST = 5.0f; + const float VOXEL_GRAB_THRUST = 0.0f; if (_mousePressed && (_mouseVoxel.s != 0)) { glm::vec2 mouseDrag(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY); glm::quat orientation = _myAvatar.getOrientation(); @@ -1833,7 +1866,7 @@ void Application::update(float deltaTime) { _myCamera.setModeShiftRate(1.0f); } } else { - const float THIRD_PERSON_SHIFT_VELOCITY = 2.0f; + const float THIRD_PERSON_SHIFT_VELOCITY = 1000.0f; const float TIME_BEFORE_SHIFT_INTO_FIRST_PERSON = 0.75f; const float TIME_BEFORE_SHIFT_INTO_THIRD_PERSON = 0.1f; @@ -2909,6 +2942,8 @@ void Application::loadSettings(QSettings* settings) { _headCameraPitchYawScale = loadSetting(settings, "headCameraPitchYawScale", 0.0f); _audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0); + _horizontalFieldOfView = loadSetting(settings, "horizontalFieldOfView", HORIZONTAL_FIELD_OF_VIEW_DEGREES); + settings->beginGroup("View Frustum Offset Camera"); // in case settings is corrupt or missing loadSetting() will check for NaN _viewFrustumOffsetYaw = loadSetting(settings, "viewFrustumOffsetYaw" , 0.0f); @@ -2930,6 +2965,7 @@ void Application::saveSettings(QSettings* settings) { settings->setValue("headCameraPitchYawScale", _headCameraPitchYawScale); settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples); + settings->setValue("horizontalFieldOfView", _horizontalFieldOfView); settings->beginGroup("View Frustum Offset Camera"); settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffsetYaw); settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffsetPitch); diff --git a/interface/src/Application.h b/interface/src/Application.h index e2b0cd21f4..fd3742fcd7 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -297,6 +297,8 @@ private: int _audioJitterBufferSamples; // Number of extra samples to wait before starting audio playback + float _horizontalFieldOfView; // In Degrees, doesn't apply to HMD like Oculus + HandControl _handControl; int _mouseX; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index cfc4f6de19..e673d2b1b1 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -348,12 +348,12 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) : inputParameters.channelCount = 2; // Stereo input inputParameters.sampleFormat = (paInt16 | paNonInterleaved); - inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency; + inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; inputParameters.hostApiSpecificStreamInfo = NULL; outputParameters.channelCount = 2; // Stereo output outputParameters.sampleFormat = (paInt16 | paNonInterleaved); - outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; + outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; outputPortAudioError(Pa_OpenStream(&_stream, diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 4382a86d87..a3c8cf1046 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -10,7 +10,6 @@ #define __interface__Audio__ #include - #include #include diff --git a/interface/src/Balls.cpp b/interface/src/Balls.cpp index 8b3f45ce40..20613b3448 100644 --- a/interface/src/Balls.cpp +++ b/interface/src/Balls.cpp @@ -19,7 +19,7 @@ #include "world.h" const float INITIAL_AREA = 0.2f; -const float BALL_RADIUS = 0.025f; +const float BALL_RADIUS = 0.016f; const glm::vec3 INITIAL_COLOR(0.62f, 0.74f, 0.91f); Balls::Balls(int numberOfBalls) { @@ -83,8 +83,9 @@ void Balls::render() { } const float CONSTANT_VELOCITY_DAMPING = 1.0f; -const float NOISE_SCALE = 0.00; -const float SPRING_FORCE = 1.0; +const float NOISE_SCALE = 0.06; +const float SPRING_FORCE = 30.0; +const float ORIGIN_DISTANCE = 0.1; const float SPRING_DAMPING = 1.0; void Balls::simulate(float deltaTime) { @@ -100,10 +101,21 @@ void Balls::simulate(float deltaTime) { // Add noise _balls[i].velocity += randVector() * NOISE_SCALE; + // spring force to origin + float separation = glm::distance(_balls[i].position, + _origin); + + _balls[i].velocity += + glm::normalize(_balls[i].position - _origin) + * deltaTime + * + SPRING_FORCE * + (ORIGIN_DISTANCE - separation); + // Approach target position - for (unsigned int i = 0; i < _numberOfBalls; ++i) { - _balls[i].position += randFloat() * deltaTime * (_balls[i].targetPosition - _balls[i].position); - } +// for (unsigned int i = 0; i < _numberOfBalls; ++i) { +// _balls[i].position += randFloat() * deltaTime * (_balls[i].targetPosition - _balls[i].position); +// } // Spring Force diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index e2705ab02a..868de81bfe 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -25,6 +25,7 @@ const float CAMERA_MIRROR_MODE_UP_SHIFT = 0.0f; const float CAMERA_MIRROR_MODE_DISTANCE = 0.3f; const float CAMERA_MIRROR_MODE_TIGHTNESS = 100.0f; + Camera::Camera() { _needsToInitialize = true; @@ -35,7 +36,7 @@ Camera::Camera() { _linearModeShift = 0.0f; _mode = CAMERA_MODE_THIRD_PERSON; _tightness = 10.0f; // default - _fieldOfView = 60.0f; // default + _fieldOfView = HORIZONTAL_FIELD_OF_VIEW_DEGREES; _nearClip = 0.08f; // default _farClip = 50.0f * TREE_SCALE; // default _upShift = 0.0f; diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 1f36d33f58..5e0dfaff95 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -11,6 +11,8 @@ #include #include +const float HORIZONTAL_FIELD_OF_VIEW_DEGREES = 90.0f; + enum CameraMode { CAMERA_MODE_NULL = -1, diff --git a/libraries/voxels/src/SquarePixelMap.cpp b/libraries/voxels/src/SquarePixelMap.cpp new file mode 100644 index 0000000000..bdc97624a7 --- /dev/null +++ b/libraries/voxels/src/SquarePixelMap.cpp @@ -0,0 +1,239 @@ +// +// SquarePixelMap.cpp +// hifi +// +// Created by Tomáš Horáček on 6/25/13. +// +// + +#include "SquarePixelMap.h" +#include +#include + +#define CHILD_COORD_X_IS_1 0x1 +#define CHILD_COORD_Y_IS_1 0x2 +#define ALPHA_CHANNEL_RANGE_FLOAT 256.f +#define ALPHA_CHANNEL_BIT_OFFSET 24 +#define RED_CHANNEL_BIT_OFFSET 16 +#define GREEN_CHANNEL_BIT_OFFSET 8 + +unsigned int numberOfBitsForSize(unsigned int size) { + if (size == 0) { + return 0; + } + + size--; + + unsigned int ans = 1; + while (size >>= 1) { + ans++; + } + return ans; +} + +struct PixelQuadTreeCoordinates { + unsigned int x; + unsigned int y; + unsigned int size; +}; + +class PixelQuadTreeNode { +public: + PixelQuadTreeCoordinates _coord; + uint32_t _color; // undefined value for _allChildrenHasSameColor = false + bool _allChildrenHasSameColor; + uint8_t _minimumNeighbourhoodAplha; + + // 0 x -> 1 + // +---+---+ + // y | 0 | 1 | <- child index + // | +---+---+ + // v | 2 | 3 | + // +---+---+ + // 1 + PixelQuadTreeNode* _children[4]; + + PixelQuadTreeNode(PixelQuadTreeCoordinates coord, SquarePixelMap* pixelMap); + ~PixelQuadTreeNode() { + for (int i = 0; i < 4; i++) { + delete _children[i]; + } + } + +private: + void updateChildCoordinates(int i, PixelQuadTreeCoordinates& childCoord) { + childCoord.x = _coord.x; + childCoord.y = _coord.y; + + if (i & CHILD_COORD_X_IS_1) { + childCoord.x += childCoord.size; + } + if (i & CHILD_COORD_Y_IS_1) { + childCoord.y += childCoord.size; + } + } + + bool hasAllChildrenSameColor() { + return false; //turn off import voxel grouping + + for (int i = 1; i < 4; i++) { + if (!_children[i]->_allChildrenHasSameColor) { + return false; + } + } + + uint32_t firstColor = _children[0]->_color; + + for (int i = 1; i < 4; i++) { + if (firstColor != _children[i]->_color) { + return false; + } + } + return true; + } +}; + +PixelQuadTreeNode::PixelQuadTreeNode(PixelQuadTreeCoordinates coord, SquarePixelMap* pixelMap) : _coord(coord), _minimumNeighbourhoodAplha(-1) { + for (int i = 0; i < 4; i++) { + _children[i] = NULL; + } + + if (_coord.size == 1) { + _color = pixelMap->getPixelAt(_coord.x, _coord.y); + + _minimumNeighbourhoodAplha = std::min(pixelMap->getAlphaAt(_coord.x + 1, _coord.y), _minimumNeighbourhoodAplha); + _minimumNeighbourhoodAplha = std::min(pixelMap->getAlphaAt(_coord.x - 1, _coord.y), _minimumNeighbourhoodAplha); + _minimumNeighbourhoodAplha = std::min(pixelMap->getAlphaAt(_coord.x, _coord.y + 1), _minimumNeighbourhoodAplha); + _minimumNeighbourhoodAplha = std::min(pixelMap->getAlphaAt(_coord.x, _coord.y - 1), _minimumNeighbourhoodAplha); + + _allChildrenHasSameColor = true; + } else { + PixelQuadTreeCoordinates childCoord = PixelQuadTreeCoordinates(); + childCoord.size = _coord.size / 2; + + for (int i = 0; i < 4; i++) { + this->updateChildCoordinates(i, childCoord); + + + if (childCoord.x < pixelMap->dimension() && + childCoord.y < pixelMap->dimension()) { + + _children[i] = new PixelQuadTreeNode(childCoord, pixelMap); + } + } + + if (this->hasAllChildrenSameColor()) { + _allChildrenHasSameColor = true; + _color = _children[0]->_color; + + _minimumNeighbourhoodAplha = _children[0]->_minimumNeighbourhoodAplha; + + for (int i = 0; i < 4; i++) { + _minimumNeighbourhoodAplha = std::min(_children[i]->_minimumNeighbourhoodAplha, _minimumNeighbourhoodAplha); + delete _children[i]; + _children[i] = NULL; + } + } else { + _allChildrenHasSameColor = false; + } + } +} + +SquarePixelMap::SquarePixelMap(const uint32_t* pixels, int dimension) : _rootPixelQuadTreeNode(NULL) { + _data = new SquarePixelMapData(); + _data->dimension = dimension; + _data->reference_counter = 1; + + size_t pixels_size = dimension * dimension; + _data->pixels = new uint32_t[pixels_size]; + memcpy((void*)_data->pixels, (void*)pixels, sizeof(uint32_t) * pixels_size); +} + +SquarePixelMap::SquarePixelMap(const SquarePixelMap& other) { + this->_data = other._data; + this->_data->reference_counter++; +} + +SquarePixelMap::~SquarePixelMap() { + delete _rootPixelQuadTreeNode; + + if (--_data->reference_counter == 0) { + delete _data->pixels; + delete _data; + } +} + +void SquarePixelMap::addVoxelsToVoxelTree(VoxelTree* voxelTree) { + this->generateRootPixelQuadTreeNode(); + this->createVoxelsFromPixelQuadTreeToVoxelTree(_rootPixelQuadTreeNode, voxelTree); +} + +int SquarePixelMap::dimension() { + return _data->dimension; +} + +uint32_t SquarePixelMap::getPixelAt(unsigned int x, unsigned int y) { + return _data->pixels[x + y * _data->dimension]; +} + +uint8_t SquarePixelMap::getAlphaAt(int x, int y) { + int max_coord = this->dimension() - 1; + + if (x < 0 || y < 0 || x > max_coord || y > max_coord) { + return -1; + } + + return this->getPixelAt(x, y) >> ALPHA_CHANNEL_BIT_OFFSET; +} + +void SquarePixelMap::generateRootPixelQuadTreeNode() { + delete _rootPixelQuadTreeNode; + + PixelQuadTreeCoordinates rootNodeCoord = PixelQuadTreeCoordinates(); + rootNodeCoord.size = 1 << numberOfBitsForSize(_data->dimension); + rootNodeCoord.x = rootNodeCoord.y = 0; + + _rootPixelQuadTreeNode = new PixelQuadTreeNode(rootNodeCoord, this); +} + +void SquarePixelMap::createVoxelsFromPixelQuadTreeToVoxelTree(PixelQuadTreeNode* pixelQuadTreeNode, VoxelTree* voxelTree) { + if (pixelQuadTreeNode->_allChildrenHasSameColor) { + VoxelDetail voxel = this->getVoxelDetail(pixelQuadTreeNode); + + unsigned char minimumNeighbourhoodAplha = std::max(0, pixelQuadTreeNode->_minimumNeighbourhoodAplha - 1); + + float minimumNeighbourhoodY = voxel.s * (floor(minimumNeighbourhoodAplha / (ALPHA_CHANNEL_RANGE_FLOAT * voxel.s)) + 0.5); + + do { + voxelTree->createVoxel(voxel.x, voxel.y, voxel.z, voxel.s, voxel.red, voxel.green, voxel.blue, true); + } while ((voxel.y -= voxel.s) > minimumNeighbourhoodY); + } else { + for (int i = 0; i < 4; i++) { + PixelQuadTreeNode* child = pixelQuadTreeNode->_children[i]; + if (child) { + this->createVoxelsFromPixelQuadTreeToVoxelTree(child, voxelTree); + } + } + } +} + +VoxelDetail SquarePixelMap::getVoxelDetail(PixelQuadTreeNode* pixelQuadTreeNode) { + VoxelDetail voxel = VoxelDetail(); + + uint32_t color = pixelQuadTreeNode->_color; + unsigned char alpha = std::max(0, (color >> ALPHA_CHANNEL_BIT_OFFSET) - 1); + + voxel.red = color >> RED_CHANNEL_BIT_OFFSET; + voxel.green = color >> GREEN_CHANNEL_BIT_OFFSET; + voxel.blue = color; + + + float rootSize = _rootPixelQuadTreeNode->_coord.size; + + voxel.s = pixelQuadTreeNode->_coord.size / rootSize; + voxel.y = voxel.s * (floor(alpha / (ALPHA_CHANNEL_RANGE_FLOAT * voxel.s)) + 0.5); + voxel.x = pixelQuadTreeNode->_coord.x / rootSize + voxel.s / 2; + voxel.z = pixelQuadTreeNode->_coord.y / rootSize + voxel.s / 2; + + return voxel; +} diff --git a/libraries/voxels/src/SquarePixelMap.h b/libraries/voxels/src/SquarePixelMap.h new file mode 100644 index 0000000000..0ef6dc55ee --- /dev/null +++ b/libraries/voxels/src/SquarePixelMap.h @@ -0,0 +1,44 @@ +// +// SquarePixelMap.h +// hifi +// +// Created by Tomáš Horáček on 6/25/13. +// +// + +#ifndef __hifi__SquarePixelMap__ +#define __hifi__SquarePixelMap__ + +#include +#include "VoxelTree.h" +#include "SharedUtil.h" + +class PixelQuadTreeNode; + +struct SquarePixelMapData { + const uint32_t* pixels; + int dimension; + int reference_counter; +}; + +class SquarePixelMap { +public: + SquarePixelMap(const uint32_t* pixels, int dimension); + SquarePixelMap(const SquarePixelMap& other); + ~SquarePixelMap(); + + void addVoxelsToVoxelTree(VoxelTree* voxelTree); + + int dimension(); + uint32_t getPixelAt(unsigned int x, unsigned int y); + uint8_t getAlphaAt(int x, int y); +private: + SquarePixelMapData* _data; + PixelQuadTreeNode* _rootPixelQuadTreeNode; + + void generateRootPixelQuadTreeNode(); + void createVoxelsFromPixelQuadTreeToVoxelTree(PixelQuadTreeNode* pixelQuadTreeNode, VoxelTree* voxelTree); + VoxelDetail getVoxelDetail(PixelQuadTreeNode* pixelQuadTreeNode); +}; + +#endif /* defined(__hifi__SquarePixelMap__) */ diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index e2f66fa52b..b16425370e 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -23,6 +23,8 @@ #include // to load voxels from file #include "VoxelConstants.h" #include "CoverageMap.h" +#include "SquarePixelMap.h" + #include @@ -1447,6 +1449,12 @@ bool VoxelTree::readFromSVOFile(const char* fileName) { return false; } +bool VoxelTree::readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension) { + SquarePixelMap pixelMap = SquarePixelMap(pixels, dimension); + pixelMap.addVoxelsToVoxelTree(this); + return true; +} + void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) const { std::ofstream file(fileName, std::ios::out|std::ios::binary); diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index df721d0ba5..72ea9e2326 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -133,6 +133,8 @@ public: // these will read/write files that match the wireformat, excluding the 'V' leading void writeToSVOFile(const char* filename, VoxelNode* node = NULL) const; bool readFromSVOFile(const char* filename); + // reads voxels from square image with alpha as a Y-axis + bool readFromSquareARGB32Pixels(const uint32_t* pixels, int dimension); unsigned long getVoxelCount();