diff --git a/interface/resources/shaders/passthrough.vert b/interface/resources/shaders/passthrough.vert new file mode 100644 index 0000000000..f3d5c5b504 --- /dev/null +++ b/interface/resources/shaders/passthrough.vert @@ -0,0 +1,10 @@ +#version 120 + +attribute float voxelSizeIn; +varying float voxelSize; + +void main(void) { + gl_FrontColor = gl_Color; + gl_Position = gl_Vertex; // don't call ftransform(), because we do projection in geometry shader + voxelSize = voxelSizeIn; +} \ No newline at end of file diff --git a/interface/resources/shaders/voxel.geom b/interface/resources/shaders/voxel.geom new file mode 100644 index 0000000000..13b502357e --- /dev/null +++ b/interface/resources/shaders/voxel.geom @@ -0,0 +1,67 @@ +#version 120 +#extension GL_ARB_geometry_shader4 : enable + +// +// VOXEL GEOMETRY SHADER +// +// Input: gl_VerticesIn/gl_PositionIn +// GL_POINTS +// Assumes vertex shader has not transformed coordinates +// Each gl_PositionIn is the corner of voxel +// varying voxelSize - which is the voxel size +// +// Note: In vertex shader doesn't do any transform. Therefore passing the 3D world coordinates xyz to us +// +// Output: GL_TRIANGLE_STRIP +// +// Issues: +// how do we need to handle lighting of these colors?? +// how do we handle normals? +// check for size=0 and don't output the primitive +// + +varying in float voxelSize[1]; + +const int VERTICES_PER_FACE = 4; +const int COORD_PER_VERTEX = 3; +const int COORD_PER_FACE = COORD_PER_VERTEX * VERTICES_PER_FACE; + +void faceOfVoxel(vec4 corner, float scale, float[COORD_PER_FACE] facePoints, vec4 color) { + for (int v = 0; v < VERTICES_PER_FACE; v++ ) { + vec4 vertex = corner; + for (int c = 0; c < COORD_PER_VERTEX; c++ ) { + int cIndex = c + (v * COORD_PER_VERTEX); + vertex[c] += (facePoints[cIndex] * scale); + } + gl_FrontColor = color; + gl_Position = gl_ModelViewProjectionMatrix * vertex; + EmitVertex(); + } + EndPrimitive(); +} + + +void main() { + //increment variable + int i; + vec4 corner; + float scale; + + float bottomFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1 ); + float topFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1 ); + float rightFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1 ); + float leftFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1 ); + float frontFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 ); + float backFace[COORD_PER_FACE] = float[COORD_PER_FACE]( 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1 ); + + for(i = 0; i < gl_VerticesIn; i++) { + corner = gl_PositionIn[i]; + scale = voxelSize[i]; + faceOfVoxel(corner, scale, bottomFace, gl_FrontColorIn[i]); + faceOfVoxel(corner, scale, topFace, gl_FrontColorIn[i]); + faceOfVoxel(corner, scale, rightFace, gl_FrontColorIn[i]); + faceOfVoxel(corner, scale, leftFace, gl_FrontColorIn[i]); + faceOfVoxel(corner, scale, frontFace, gl_FrontColorIn[i]); + faceOfVoxel(corner, scale, backFace, gl_FrontColorIn[i]); + } +} \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 47544e408a..8426b1fd55 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -389,6 +389,7 @@ void Application::paintGL() { } else { _glowEffect.prepare(); + glMatrixMode(GL_MODELVIEW); glPushMatrix(); @@ -541,6 +542,10 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar.setDriveKeys(UP, 1); } break; + + case Qt::Key_Asterisk: + Menu::getInstance()->triggerOption(MenuOption::Stars); + break; case Qt::Key_C: if (isShifted) { @@ -810,8 +815,6 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_F: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::DisplayFrustum); - } else { - Menu::getInstance()->triggerOption(MenuOption::Fullscreen); } break; case Qt::Key_V: @@ -1518,7 +1521,6 @@ void Application::initDisplay() { } void Application::init() { - _voxels.init(); _sharedVoxelSystemViewFrustum.setPosition(glm::vec3(TREE_SCALE / 2.0f, TREE_SCALE / 2.0f, 3.0f * TREE_SCALE / 2.0f)); @@ -1539,6 +1541,7 @@ void Application::init() { _glowEffect.init(); _ambientOcclusionEffect.init(); + _voxelShader.init(); _handControl.setScreenDimensions(_glWidget->width(), _glWidget->height()); @@ -1569,9 +1572,13 @@ void Application::init() { if (Menu::getInstance()->getAudioJitterBufferSamples() != 0) { _audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples()); } - qDebug("Loaded settings.\n"); + // Set up VoxelSystem after loading preferences so we can get the desired max voxel count + _voxels.setMaxVoxels(Menu::getInstance()->getMaxVoxels()); + _voxels.init(); + + Avatar::sendAvatarURLsMessage(_myAvatar.getVoxels()->getVoxelURL(), _myAvatar.getHead().getBlendFace().getModelURL()); _palette.init(_glWidget->width(), _glWidget->height()); @@ -2565,7 +2572,7 @@ void Application::displaySide(Camera& whichCamera) { if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { renderViewFrustum(_viewFrustum); } - + // render voxel fades if they exist if (_voxelFades.size() > 0) { for(std::vector::iterator fade = _voxelFades.begin(); fade != _voxelFades.end();) { @@ -2810,7 +2817,10 @@ void Application::displayStats() { std::stringstream voxelStats; voxelStats.precision(4); - voxelStats << "Voxels Rendered: " << _voxels.getVoxelsRendered() / 1000.f << "K Updated: " << _voxels.getVoxelsUpdated()/1000.f << "K"; + voxelStats << "Voxels Rendered: " << _voxels.getVoxelsRendered() / 1000.f << "K " << + "Updated: " << _voxels.getVoxelsUpdated()/1000.f << "K " << + "Max: " << _voxels.getMaxVoxels()/1000.f << "K "; + drawtext(10, statsVerticalOffset + 230, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); voxelStats.str(""); diff --git a/interface/src/Application.h b/interface/src/Application.h index 5541f84573..a8e84e5c2a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -51,6 +51,7 @@ #include "renderer/AmbientOcclusionEffect.h" #include "renderer/GeometryCache.h" #include "renderer/GlowEffect.h" +#include "renderer/VoxelShader.h" #include "renderer/TextureCache.h" #include "ui/BandwidthDialog.h" #include "ui/ChatEntry.h" @@ -144,6 +145,8 @@ public: virtual void nodeAdded(Node* node); virtual void nodeKilled(Node* node); virtual void packetSentNotification(ssize_t length); + + VoxelShader& getVoxelShader() { return _voxelShader; } public slots: void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data); @@ -346,6 +349,7 @@ private: GlowEffect _glowEffect; AmbientOcclusionEffect _ambientOcclusionEffect; + VoxelShader _voxelShader; #ifndef _WIN32 Audio _audio; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 77d00c393b..0e796e28c2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -49,7 +49,8 @@ Menu::Menu() : _frustumDrawMode(FRUSTUM_DRAW_MODE_ALL), _viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET), _voxelModeActionsGroup(NULL), - _voxelStatsDialog(NULL) + _voxelStatsDialog(NULL), + _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM) { Application *appInstance = Application::getInstance(); @@ -189,50 +190,36 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Fullscreen, - Qt::Key_F, + Qt::CTRL | Qt::META | Qt::Key_F, false, appInstance, SLOT(setFullscreen(bool))); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::Key_H); - addActionToQMenuAndActionHash(viewMenu, + QMenu* avatarSizeMenu = viewMenu->addMenu("Avatar Size"); + + addActionToQMenuAndActionHash(avatarSizeMenu, MenuOption::IncreaseAvatarSize, Qt::Key_Plus, appInstance->getAvatar(), SLOT(increaseSize())); - addActionToQMenuAndActionHash(viewMenu, + addActionToQMenuAndActionHash(avatarSizeMenu, MenuOption::DecreaseAvatarSize, Qt::Key_Minus, appInstance->getAvatar(), SLOT(decreaseSize())); - addActionToQMenuAndActionHash(viewMenu, + addActionToQMenuAndActionHash(avatarSizeMenu, 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))); - - addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::LEDTracking, - 0, - false, - appInstance->getWebcam()->getGrabber(), - SLOT(setLEDTrackingOn(bool))); - + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false); - + addDisabledActionAndSeparator(viewMenu, "Stats"); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L); @@ -242,50 +229,120 @@ Menu::Menu() : addActionToQMenuAndActionHash(viewMenu, MenuOption::VoxelStats, 0, this, SLOT(voxelStatsDetails())); QMenu* developerMenu = addMenu("Developer"); - addDisabledActionAndSeparator(developerMenu, "Rendering"); - - addCheckableActionToQMenuAndActionHash(developerMenu, + + QMenu* renderOptionsMenu = developerMenu->addMenu("Rendering Options"); + + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::GroundPlane, 0, true); + addActionToQMenuAndActionHash(renderOptionsMenu, + MenuOption::GlowMode, + 0, + appInstance->getGlowEffect(), + SLOT(cycleRenderMode())); + + QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options"); + + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, 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); + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion); + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseVoxelShader, 0, + false, this, SLOT(switchVoxelShader())); - addActionToQMenuAndActionHash(developerMenu, + QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options"); + + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarAsBalls); + + addActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::VoxelMode, 0, appInstance->getAvatar()->getVoxels(), SLOT(cycleMode())); - addActionToQMenuAndActionHash(developerMenu, + addActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::FaceMode, 0, &appInstance->getAvatar()->getHead().getFace(), SLOT(cycleRenderMode())); - addActionToQMenuAndActionHash(developerMenu, - MenuOption::GlowMode, + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::UsePerlinFace, 0, false); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtIndicator, 0, true); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, + MenuOption::FaceshiftTCP, + 0, + false, + appInstance->getFaceshift(), + SLOT(setTCPEnabled(bool))); + + QMenu* webcamOptionsMenu = developerMenu->addMenu("Webcam Options"); + + addCheckableActionToQMenuAndActionHash(webcamOptionsMenu, + MenuOption::Webcam, + 0, + false, + appInstance->getWebcam(), + SLOT(setEnabled(bool))); + + addActionToQMenuAndActionHash(webcamOptionsMenu, + MenuOption::WebcamMode, 0, - appInstance->getGlowEffect(), - SLOT(cycleRenderMode())); + appInstance->getWebcam()->getGrabber(), + SLOT(cycleVideoSendMode())); + + addCheckableActionToQMenuAndActionHash(webcamOptionsMenu, + MenuOption::WebcamTexture, + 0, + false, + appInstance->getWebcam()->getGrabber(), + SLOT(setDepthOnly(bool))); + + QMenu* raveGloveOptionsMenu = developerMenu->addMenu("Rave Glove Options"); + + addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::SimulateLeapHand); + addCheckableActionToQMenuAndActionHash(raveGloveOptionsMenu, MenuOption::TestRaveGlove); + + + QMenu* gyroOptionsMenu = developerMenu->addMenu("Gyro Options"); + addCheckableActionToQMenuAndActionHash(gyroOptionsMenu, MenuOption::GyroLook, 0, true); + addCheckableActionToQMenuAndActionHash(gyroOptionsMenu, MenuOption::HeadMouse); + + + QMenu* trackingOptionsMenu = developerMenu->addMenu("Tracking Options"); + addCheckableActionToQMenuAndActionHash(trackingOptionsMenu, + MenuOption::SkeletonTracking, + 0, + false, + appInstance->getWebcam(), + SLOT(setSkeletonTrackingOn(bool))); - addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::UsePerlinFace, 0, false); - addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtVectors, 0, true); - addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::LookAtIndicator, 0, true); - addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::FrameTimer); + addCheckableActionToQMenuAndActionHash(trackingOptionsMenu, + MenuOption::LEDTracking, + 0, + false, + appInstance->getWebcam()->getGrabber(), + SLOT(setLEDTrackingOn(bool))); addDisabledActionAndSeparator(developerMenu, "Testing"); + + QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools"); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); + addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests())); + addActionToQMenuAndActionHash(timingMenu, + MenuOption::TreeStats, + Qt::SHIFT | Qt::Key_S, + appInstance->getVoxels(), + SLOT(collectStatsForTreesAndVBOs())); QMenu* frustumMenu = developerMenu->addMenu("View Frustum Debugging Tools"); addCheckableActionToQMenuAndActionHash(frustumMenu, MenuOption::DisplayFrustum, Qt::SHIFT | Qt::Key_F); - addActionToQMenuAndActionHash(frustumMenu, MenuOption::FrustumRenderMode, Qt::SHIFT | Qt::Key_R, @@ -293,12 +350,6 @@ Menu::Menu() : SLOT(cycleFrustumRenderMode())); updateFrustumRenderModeAction(); - addActionToQMenuAndActionHash(developerMenu, MenuOption::RunTimingTests, 0, this, SLOT(runTests())); - addActionToQMenuAndActionHash(developerMenu, - MenuOption::TreeStats, - Qt::SHIFT | Qt::Key_S, - appInstance->getVoxels(), - SLOT(collectStatsForTreesAndVBOs())); QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools"); addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings); @@ -338,18 +389,6 @@ Menu::Menu() : appInstance->getVoxels(), SLOT(falseColorizeInView())); - addActionToQMenuAndActionHash(renderDebugMenu, - MenuOption::FalseColorOccluded, - 0, - appInstance->getVoxels(), - SLOT(falseColorizeOccluded())); - - addActionToQMenuAndActionHash(renderDebugMenu, - MenuOption::FalseColorOccludedV2, - 0, - appInstance->getVoxels(), - SLOT(falseColorizeOccludedV2())); - addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorBySource, 0, @@ -362,32 +401,21 @@ Menu::Menu() : appInstance->getVoxels(), SLOT(trueColorize())); - - addCheckableActionToQMenuAndActionHash(developerMenu, - MenuOption::Webcam, - 0, - false, - appInstance->getWebcam(), - SLOT(setEnabled(bool))); - - addActionToQMenuAndActionHash(developerMenu, - MenuOption::WebcamMode, + addDisabledActionAndSeparator(renderDebugMenu, "Coverage Maps"); + addActionToQMenuAndActionHash(renderDebugMenu, + MenuOption::FalseColorOccluded, 0, - appInstance->getWebcam()->getGrabber(), - SLOT(cycleVideoSendMode())); - addCheckableActionToQMenuAndActionHash(developerMenu, - MenuOption::WebcamTexture, - 0, - false, - appInstance->getWebcam()->getGrabber(), - SLOT(setDepthOnly(bool))); + appInstance->getVoxels(), + SLOT(falseColorizeOccluded())); - addCheckableActionToQMenuAndActionHash(developerMenu, - MenuOption::FaceshiftTCP, - 0, - false, - appInstance->getFaceshift(), - SLOT(setTCPEnabled(bool))); + addActionToQMenuAndActionHash(renderDebugMenu, + MenuOption::FalseColorOccludedV2, + 0, + appInstance->getVoxels(), + SLOT(falseColorizeOccludedV2())); + + addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMap, Qt::SHIFT | Qt::CTRL | Qt::Key_O); + addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P); QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools"); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoAudio); @@ -407,47 +435,38 @@ Menu::Menu() : appInstance, SLOT(setListenModeSingleSource())); - addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::TestPing, 0, true); + QMenu* voxelProtoOptionsMenu = developerMenu->addMenu("Voxel Server Protocol Options"); - addCheckableActionToQMenuAndActionHash(developerMenu, + addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::SendVoxelColors, 0, true, appInstance->getAvatar(), SLOT(setWantColor(bool))); - addCheckableActionToQMenuAndActionHash(developerMenu, + addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::LowRes, 0, true, appInstance->getAvatar(), SLOT(setWantLowResMoving(bool))); - addCheckableActionToQMenuAndActionHash(developerMenu, + addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DeltaSending, 0, true, appInstance->getAvatar(), SLOT(setWantDelta(bool))); - addCheckableActionToQMenuAndActionHash(developerMenu, + addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, 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); + addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DestructiveAddVoxel); + #ifndef Q_OS_MAC QMenu* helpMenu = addMenu("Help"); QAction* helpAction = helpMenu->addAction(MenuOption::AboutApp); @@ -469,6 +488,7 @@ void Menu::loadSettings(QSettings* settings) { _gyroCameraSensitivity = loadSetting(settings, "gyroCameraSensitivity", 0.5f); _audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0); _fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES); + _maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM); settings->beginGroup("View Frustum Offset Camera"); // in case settings is corrupt or missing loadSetting() will check for NaN @@ -492,6 +512,7 @@ void Menu::saveSettings(QSettings* settings) { settings->setValue("gyroCameraSensitivity", _gyroCameraSensitivity); settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples); settings->setValue("fieldOfView", _fieldOfView); + settings->setValue("maxVoxels", _maxVoxels); settings->beginGroup("View Frustum Offset Camera"); settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw); settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffset.pitch); @@ -739,9 +760,6 @@ void Menu::editPreferences() { QFormLayout* form = new QFormLayout(); layout->addLayout(form, 1); - QLineEdit* domainServerLineEdit = lineEditForDomainHostname(); - form->addRow("Domain server:", domainServerLineEdit); - QLineEdit* avatarURL = new QLineEdit(applicationInstance->getAvatar()->getVoxels()->getVoxelURL().toString()); avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH); form->addRow("Avatar URL:", avatarURL); @@ -773,6 +791,16 @@ void Menu::editPreferences() { audioJitterBufferSamples->setMinimum(-10000); audioJitterBufferSamples->setValue(_audioJitterBufferSamples); form->addRow("Audio Jitter Buffer Samples (0 for automatic):", audioJitterBufferSamples); + + QSpinBox* maxVoxels = new QSpinBox(); + const int MAX_MAX_VOXELS = 5000000; + const int MIN_MAX_VOXELS = 0; + const int STEP_MAX_VOXELS = 50000; + maxVoxels->setMaximum(MAX_MAX_VOXELS); + maxVoxels->setMinimum(MIN_MAX_VOXELS); + maxVoxels->setSingleStep(STEP_MAX_VOXELS); + maxVoxels->setValue(_maxVoxels); + form->addRow("Maximum Voxels:", maxVoxels); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); @@ -785,8 +813,6 @@ void Menu::editPreferences() { return; } - updateDSHostname(domainServerLineEdit->text()); - QUrl avatarVoxelURL(avatarURL->text()); applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL); @@ -798,6 +824,8 @@ void Menu::editPreferences() { applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum()); _gyroCameraSensitivity = gyroCameraSensitivity->value(); + _maxVoxels = maxVoxels->value(); + applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels); applicationInstance->getAvatar()->setLeanScale(leanScale->value()); @@ -997,3 +1025,8 @@ void Menu::updateFrustumRenderModeAction() { break; } } + +void Menu::switchVoxelShader() { + Application::getInstance()->getVoxels()->setUseVoxelShader(isOptionChecked(MenuOption::UseVoxelShader)); +} + diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 78ef65221c..3f242f5fd9 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -54,6 +54,8 @@ public: FrustumDrawMode getFrustumDrawMode() const { return _frustumDrawMode; } ViewFrustumOffset getViewFrustumOffset() const { return _viewFrustumOffset; } VoxelStatsDialog* getVoxelStatsDialog() const { return _voxelStatsDialog; } + int getMaxVoxels() const { return _maxVoxels; } + void handleViewFrustumOffsetKeyModifier(int key); @@ -78,6 +80,7 @@ private slots: void chooseVoxelPaintColor(); void runTests(); void resetSwatchColors(); + void switchVoxelShader(); private: static Menu* _instance; @@ -115,6 +118,7 @@ private: ViewFrustumOffset _viewFrustumOffset; QActionGroup* _voxelModeActionsGroup; VoxelStatsDialog* _voxelStatsDialog; + int _maxVoxels; }; namespace MenuOption { @@ -201,9 +205,7 @@ namespace MenuOption { const QString TransmitterDrive = "Transmitter Drive"; const QString UsePerlinFace = "Use Perlin's Face"; const QString Quit = "Quit"; - const QString Webcam = "Webcam"; - const QString WebcamMode = "Cycle Webcam Send Mode"; - const QString WebcamTexture = "Webcam Texture"; + const QString UseVoxelShader = "Use Voxel Shader"; const QString Voxels = "Voxels"; const QString VoxelAddMode = "Add Voxel Mode"; const QString VoxelColorMode = "Color Voxel Mode"; @@ -214,6 +216,9 @@ namespace MenuOption { const QString VoxelSelectMode = "Select Voxel Mode"; const QString VoxelStats = "Voxel Stats"; const QString VoxelTextures = "Voxel Textures"; + const QString Webcam = "Webcam"; + const QString WebcamMode = "Cycle Webcam Send Mode"; + const QString WebcamTexture = "Webcam Texture"; } #endif /* defined(__hifi__Menu__) */ diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index a1595f0de0..86d8704905 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -74,6 +74,18 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); + + _useVoxelShader = false; + _writeVoxelShaderData = NULL; + _readVoxelShaderData = NULL; + + _readVerticesArray = NULL; + _writeVerticesArray = NULL; + _readColorsArray = NULL; + _writeColorsArray = NULL; + _writeVoxelDirtyArray = NULL; + _readVoxelDirtyArray = NULL; + } void VoxelSystem::nodeDeleted(VoxelNode* node) { @@ -119,21 +131,7 @@ void VoxelSystem::clearFreeBufferIndexes() { } VoxelSystem::~VoxelSystem() { - if (_initialized) { - // Destroy glBuffers - glDeleteBuffers(1, &_vboVerticesID); - glDeleteBuffers(1, &_vboNormalsID); - glDeleteBuffers(1, &_vboColorsID); - glDeleteBuffers(1, &_vboIndicesID); - - delete[] _readVerticesArray; - delete[] _writeVerticesArray; - delete[] _readColorsArray; - delete[] _writeColorsArray; - delete[] _writeVoxelDirtyArray; - delete[] _readVoxelDirtyArray; - } - + cleanupVoxelMemory(); delete _tree; pthread_mutex_destroy(&_bufferWriteLock); pthread_mutex_destroy(&_treeLock); @@ -141,6 +139,174 @@ VoxelSystem::~VoxelSystem() { VoxelNode::removeDeleteHook(this); } +void VoxelSystem::setMaxVoxels(int maxVoxels) { + pthread_mutex_lock(&_bufferWriteLock); + bool wasInitialized = _initialized; + if (wasInitialized) { + cleanupVoxelMemory(); + } + _maxVoxels = maxVoxels; + if (wasInitialized) { + init(); + } + pthread_mutex_unlock(&_bufferWriteLock); +} + +void VoxelSystem::setUseVoxelShader(bool useVoxelShader) { + pthread_mutex_lock(&_bufferWriteLock); + bool wasInitialized = _initialized; + if (wasInitialized) { + cleanupVoxelMemory(); + } + _useVoxelShader = useVoxelShader; + if (wasInitialized) { + init(); + } + pthread_mutex_unlock(&_bufferWriteLock); +} + +void VoxelSystem::cleanupVoxelMemory() { + if (_initialized) { + if (_useVoxelShader) { + // these are used when in VoxelShader mode. + glDeleteBuffers(1, &_vboVoxelsID); + glDeleteBuffers(1, &_vboVoxelsIndicesID); + + delete[] _writeVoxelShaderData; + delete[] _readVoxelShaderData; + } else { + // Destroy glBuffers + glDeleteBuffers(1, &_vboVerticesID); + glDeleteBuffers(1, &_vboNormalsID); + glDeleteBuffers(1, &_vboColorsID); + glDeleteBuffers(1, &_vboIndicesID); + + delete[] _readVerticesArray; + delete[] _writeVerticesArray; + delete[] _readColorsArray; + delete[] _writeColorsArray; + delete[] _writeVoxelDirtyArray; + delete[] _readVoxelDirtyArray; + } + } + _initialized = false; // no longer initialized +} + +void VoxelSystem::initVoxelMemory() { + if (_useVoxelShader) { + GLuint* indicesArray = new GLuint[_maxVoxels]; + + // populate the indicesArray + // this will not change given new voxels, so we can set it all up now + for (int n = 0; n < _maxVoxels; n++) { + indicesArray[n] = n; + } + + // bind the indices VBO to the actual indices array + glGenBuffers(1, &_vboVoxelsIndicesID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboVoxelsIndicesID); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, _maxVoxels, indicesArray, GL_STATIC_DRAW); + + + glGenBuffers(1, &_vboVoxelsID); + glBindBuffer(GL_ARRAY_BUFFER, _vboVoxelsID); + glBufferData(GL_ARRAY_BUFFER, _maxVoxels * sizeof(VoxelShaderVBOData), NULL, GL_DYNAMIC_DRAW); + + // delete the indices and normals arrays that are no longer needed + delete[] indicesArray; + + // we will track individual dirty sections with these arrays of bools + _writeVoxelDirtyArray = new bool[_maxVoxels]; + memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); + _readVoxelDirtyArray = new bool[_maxVoxels]; + memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); + + // prep the data structures for incoming voxel data + _writeVoxelShaderData = new VoxelShaderVBOData[_maxVoxels]; + _readVoxelShaderData = new VoxelShaderVBOData[_maxVoxels]; + } else { + GLuint* indicesArray = new GLuint[INDICES_PER_VOXEL * _maxVoxels]; + + // populate the indicesArray + // this will not change given new voxels, so we can set it all up now + for (int n = 0; n < _maxVoxels; n++) { + // fill the indices array + int voxelIndexOffset = n * INDICES_PER_VOXEL; + GLuint* currentIndicesPos = indicesArray + voxelIndexOffset; + int startIndex = (n * VERTICES_PER_VOXEL); + + for (int i = 0; i < INDICES_PER_VOXEL; i++) { + // add indices for this side of the cube + currentIndicesPos[i] = startIndex + identityIndices[i]; + } + } + + GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; + GLfloat* normalsArrayEndPointer = normalsArray; + + // populate the normalsArray + for (int n = 0; n < _maxVoxels; n++) { + for (int i = 0; i < VERTEX_POINTS_PER_VOXEL; i++) { + *(normalsArrayEndPointer++) = identityNormals[i]; + } + } + + glGenBuffers(1, &_vboVerticesID); + glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); + glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW); + + // VBO for the normalsArray + glGenBuffers(1, &_vboNormalsID); + glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); + glBufferData(GL_ARRAY_BUFFER, + VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, + normalsArray, GL_STATIC_DRAW); + + // VBO for colorsArray + glGenBuffers(1, &_vboColorsID); + glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); + glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW); + + // VBO for the indicesArray + glGenBuffers(1, &_vboIndicesID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + INDICES_PER_VOXEL * sizeof(GLuint) * _maxVoxels, + indicesArray, GL_STATIC_DRAW); + + // delete the indices and normals arrays that are no longer needed + delete[] indicesArray; + delete[] normalsArray; + + + // we will track individual dirty sections with these arrays of bools + _writeVoxelDirtyArray = new bool[_maxVoxels]; + memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); + _readVoxelDirtyArray = new bool[_maxVoxels]; + memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); + + // prep the data structures for incoming voxel data + _writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; + _readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; + + _writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; + _readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; + + + // create our simple fragment shader if we're the first system to init + if (!_perlinModulateProgram.isLinked()) { + switchToResourcesParentIfRequired(); + _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/perlin_modulate.vert"); + _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/perlin_modulate.frag"); + _perlinModulateProgram.link(); + + _perlinModulateProgram.bind(); + _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); + _perlinModulateProgram.release(); + } + } +} + void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { _tree->loadVoxelsFile(fileName, wantColorRandomizer); setupNewVoxelsForDrawing(); @@ -398,18 +564,24 @@ void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() { void VoxelSystem::copyWrittenDataSegmentToReadArrays(glBufferIndex segmentStart, glBufferIndex segmentEnd) { int segmentLength = (segmentEnd - segmentStart) + 1; + if (_useVoxelShader) { + GLsizeiptr segmentSizeBytes = segmentLength * sizeof(VoxelShaderVBOData); + void* readDataAt = &_readVoxelShaderData[segmentStart]; + void* writeDataAt = &_writeVoxelShaderData[segmentStart]; + memcpy(readDataAt, writeDataAt, segmentSizeBytes); + } else { + GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); + GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); + GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); + GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); + memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes); - GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); - GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); - GLfloat* readVerticesAt = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); - GLfloat* writeVerticesAt = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); - memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes); - - segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); - segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); - GLubyte* readColorsAt = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); - GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); - memcpy(readColorsAt, writeColorsAt, segmentSizeBytes); + segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); + segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); + GLubyte* readColorsAt = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); + GLubyte* writeColorsAt = _writeColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); + memcpy(readColorsAt, writeColorsAt, segmentSizeBytes); + } } void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) { @@ -532,11 +704,23 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) { void VoxelSystem::updateNodeInArrays(glBufferIndex nodeIndex, const glm::vec3& startVertex, float voxelScale, const nodeColor& color) { - for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) { - GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL); - GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL); - *(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale); - *(writeColorsAt +j) = color[j % 3]; + + if (_useVoxelShader) { + VoxelShaderVBOData* writeVerticesAt = &_writeVoxelShaderData[nodeIndex]; + writeVerticesAt->x = startVertex.x * TREE_SCALE; + writeVerticesAt->y = startVertex.y * TREE_SCALE; + writeVerticesAt->z = startVertex.z * TREE_SCALE; + writeVerticesAt->s = voxelScale * TREE_SCALE; + writeVerticesAt->r = color[RED_INDEX]; + writeVerticesAt->g = color[GREEN_INDEX]; + writeVerticesAt->b = color[BLUE_INDEX]; + } else { + for (int j = 0; j < VERTEX_POINTS_PER_VOXEL; j++ ) { + GLfloat* writeVerticesAt = _writeVerticesArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL); + GLubyte* writeColorsAt = _writeColorsArray + (nodeIndex * VERTEX_POINTS_PER_VOXEL); + *(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale); + *(writeColorsAt +j) = color[j % 3]; + } } } @@ -563,86 +747,8 @@ void VoxelSystem::init() { _voxelsInWriteArrays = 0; _voxelsInReadArrays = 0; - GLuint* indicesArray = new GLuint[INDICES_PER_VOXEL * _maxVoxels]; - - // populate the indicesArray - // this will not change given new voxels, so we can set it all up now - for (int n = 0; n < _maxVoxels; n++) { - // fill the indices array - int voxelIndexOffset = n * INDICES_PER_VOXEL; - GLuint* currentIndicesPos = indicesArray + voxelIndexOffset; - int startIndex = (n * VERTICES_PER_VOXEL); - - for (int i = 0; i < INDICES_PER_VOXEL; i++) { - // add indices for this side of the cube - currentIndicesPos[i] = startIndex + identityIndices[i]; - } - } - - GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; - GLfloat* normalsArrayEndPointer = normalsArray; - - // populate the normalsArray - for (int n = 0; n < _maxVoxels; n++) { - for (int i = 0; i < VERTEX_POINTS_PER_VOXEL; i++) { - *(normalsArrayEndPointer++) = identityNormals[i]; - } - } - // VBO for the verticesArray - glGenBuffers(1, &_vboVerticesID); - glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); - glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW); - - // VBO for the normalsArray - glGenBuffers(1, &_vboNormalsID); - glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); - glBufferData(GL_ARRAY_BUFFER, - VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, - normalsArray, GL_STATIC_DRAW); - - // VBO for colorsArray - glGenBuffers(1, &_vboColorsID); - glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); - glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW); - - // VBO for the indicesArray - glGenBuffers(1, &_vboIndicesID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - INDICES_PER_VOXEL * sizeof(GLuint) * _maxVoxels, - indicesArray, GL_STATIC_DRAW); - - // delete the indices and normals arrays that are no longer needed - delete[] indicesArray; - delete[] normalsArray; - - - // we will track individual dirty sections with these arrays of bools - _writeVoxelDirtyArray = new bool[_maxVoxels]; - memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); - _readVoxelDirtyArray = new bool[_maxVoxels]; - memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); - - // prep the data structures for incoming voxel data - _writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; - _readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; - - _writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; - _readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; - - - // create our simple fragment shader if we're the first system to init - if (!_perlinModulateProgram.isLinked()) { - switchToResourcesParentIfRequired(); - _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/perlin_modulate.vert"); - _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/perlin_modulate.frag"); - _perlinModulateProgram.link(); - - _perlinModulateProgram.bind(); - _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); - _perlinModulateProgram.release(); - } + initVoxelMemory(); _initialized = true; } @@ -714,17 +820,27 @@ void VoxelSystem::updateVBOs() { } void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) { - int segmentLength = (segmentEnd - segmentStart) + 1; - GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); - GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); - GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); - glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); - glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); - segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); - segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); - GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); - glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); - glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); + if (_useVoxelShader) { + int segmentLength = (segmentEnd - segmentStart) + 1; + GLintptr segmentStartAt = segmentStart * sizeof(VoxelShaderVBOData); + GLsizeiptr segmentSizeBytes = segmentLength * sizeof(VoxelShaderVBOData); + void* readVerticesFrom = &_readVoxelShaderData[segmentStart]; + + glBindBuffer(GL_ARRAY_BUFFER, _vboVoxelsID); + glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); + } else { + int segmentLength = (segmentEnd - segmentStart) + 1; + GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); + GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); + GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); + glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); + glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); + segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); + segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); + GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); + glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); + glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); + } } void VoxelSystem::render(bool texture) { @@ -734,43 +850,74 @@ void VoxelSystem::render(bool texture) { pthread_mutex_lock(&_bufferWriteLock); updateVBOs(); + + if (_useVoxelShader) { - // tell OpenGL where to find vertex and color information - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); + Application::getInstance()->getVoxelShader().begin(); - glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); - glVertexPointer(3, GL_FLOAT, 0, 0); + //Define this somewhere in your header file + #define BUFFER_OFFSET(i) ((void*)(i)) - glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); - glNormalPointer(GL_FLOAT, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, _vboVoxelsID); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(0)); //The starting point of the VBO, for the vertices + int loc = Application::getInstance()->getVoxelShader().attributeLocation("voxelSizeIn"); + glEnableVertexAttribArray(loc); + glVertexAttribPointer(loc, 1, GL_FLOAT, false, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(3*sizeof(float))); + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VoxelShaderVBOData), BUFFER_OFFSET(4*sizeof(float)));//The starting point of colors - glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); - glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboVoxelsIndicesID); + glDrawElements(GL_POINTS, _voxelsInReadArrays, GL_UNSIGNED_INT, BUFFER_OFFSET(0)); //The starting point of the IBO - applyScaleAndBindProgram(texture); + // deactivate vertex and color arrays after drawing + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + // bind with 0 to switch back to normal operation + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + Application::getInstance()->getVoxelShader().end(); + + } else { + // tell OpenGL where to find vertex and color information + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); + glVertexPointer(3, GL_FLOAT, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); + glNormalPointer(GL_FLOAT, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); + glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); + + applyScaleAndBindProgram(texture); - // for performance, enable backface culling - glEnable(GL_CULL_FACE); + // for performance, enable backface culling + glEnable(GL_CULL_FACE); - // draw the number of voxels we have - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, - 36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); + // draw the number of voxels we have + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); + glDrawRangeElementsEXT(GL_TRIANGLES, 0, VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, + 36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); - glDisable(GL_CULL_FACE); + glDisable(GL_CULL_FACE); - removeScaleAndReleaseProgram(texture); + removeScaleAndReleaseProgram(texture); - // deactivate vertex and color arrays after drawing - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); + // deactivate vertex and color arrays after drawing + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); - // bind with 0 to switch back to normal operation - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + // bind with 0 to switch back to normal operation + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } pthread_mutex_unlock(&_bufferWriteLock); } @@ -1203,7 +1350,7 @@ public: duplicateVBOIndex(0), leafNodes(0) { - memset(hasIndexFound, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool)); + memset(hasIndexFound, false, DEFAULT_MAX_VOXELS_PER_SYSTEM * sizeof(bool)); }; unsigned long totalNodes; @@ -1218,7 +1365,7 @@ public: unsigned long expectedMax; - bool hasIndexFound[MAX_VOXELS_PER_SYSTEM]; + bool hasIndexFound[DEFAULT_MAX_VOXELS_PER_SYSTEM]; }; bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) { @@ -1293,7 +1440,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() { glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; glBufferIndex maxInVBO = 0; - for (glBufferIndex i = 0; i < MAX_VOXELS_PER_SYSTEM; i++) { + for (glBufferIndex i = 0; i < DEFAULT_MAX_VOXELS_PER_SYSTEM; i++) { if (args.hasIndexFound[i]) { minInVBO = std::min(minInVBO,i); maxInVBO = std::max(maxInVBO,i); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 5ac5310b76..7a44034590 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -23,15 +23,24 @@ #include "Camera.h" #include "Util.h" #include "world.h" +#include "renderer/VoxelShader.h" class ProgramObject; const int NUM_CHILDREN = 8; +struct VoxelShaderVBOData +{ + float x, y, z; // position + float s; // size + unsigned char r,g,b; // color +}; + + class VoxelSystem : public NodeData, public VoxelNodeDeleteHook, public NodeListHook { Q_OBJECT public: - VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = MAX_VOXELS_PER_SYSTEM); + VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM); ~VoxelSystem(); void setDataSourceID(int dataSourceID) { _dataSourceID = dataSourceID; } @@ -56,6 +65,10 @@ public: bool readFromSquareARGB32Pixels(const char* filename); bool readFromSchematicFile(const char* filename); + void setUseVoxelShader(bool useVoxelShader); + + void setMaxVoxels(int maxVoxels); + long int getMaxVoxels() const { return _maxVoxels; } long int getVoxelsCreated(); long int getVoxelsColored(); long int getVoxelsBytesRead(); @@ -190,6 +203,16 @@ private: uint64_t _lastViewCulling; int _lastViewCullingElapsed; + bool getUseVoxelShader(); + void initVoxelMemory(); + void cleanupVoxelMemory(); + + bool _useVoxelShader; + GLuint _vboVoxelsID; /// when using voxel shader, we'll use this VBO + GLuint _vboVoxelsIndicesID; /// when using voxel shader, we'll use this VBO for our indexes + VoxelShaderVBOData* _writeVoxelShaderData; + VoxelShaderVBOData* _readVoxelShaderData; + GLuint _vboVerticesID; GLuint _vboNormalsID; GLuint _vboColorsID; diff --git a/interface/src/renderer/VoxelShader.cpp b/interface/src/renderer/VoxelShader.cpp new file mode 100644 index 0000000000..b8a907c917 --- /dev/null +++ b/interface/src/renderer/VoxelShader.cpp @@ -0,0 +1,69 @@ +// +// VoxelShader.cpp +// interface +// +// Created by Brad Hefta-Gaub on 9/22/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. + +// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include + +#include "Application.h" +#include "VoxelShader.h" +#include "ProgramObject.h" +#include "RenderUtil.h" + +VoxelShader::VoxelShader() + : _initialized(false) +{ + _program = NULL; +} + +VoxelShader::~VoxelShader() { + if (_initialized) { + delete _program; + } +} + +ProgramObject* VoxelShader::createGeometryShaderProgram(const QString& name) { + ProgramObject* program = new ProgramObject(); + program->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/passthrough.vert" ); + program->addShaderFromSourceFile(QGLShader::Geometry, "resources/shaders/" + name + ".geom" ); + program->setGeometryInputType(GL_POINTS); + program->setGeometryOutputType(GL_TRIANGLE_STRIP); + const int VERTICES_PER_FACE = 4; + const int FACES_PER_VOXEL = 6; + const int VERTICES_PER_VOXEL = VERTICES_PER_FACE * FACES_PER_VOXEL; + program->setGeometryOutputVertexCount(VERTICES_PER_VOXEL); + program->link(); + return program; +} + +void VoxelShader::init() { + if (_initialized) { + qDebug("[ERROR] TestProgram is already initialized.\n"); + return; + } + switchToResourcesParentIfRequired(); + _program = createGeometryShaderProgram("voxel"); + _initialized = true; +} + +void VoxelShader::begin() { + _program->bind(); +} + +void VoxelShader::end() { + _program->release(); +} + +int VoxelShader::attributeLocation(const char * name) const { + if (_program) { + return _program->attributeLocation(name); + } else { + return -1; + } +} + diff --git a/interface/src/renderer/VoxelShader.h b/interface/src/renderer/VoxelShader.h new file mode 100644 index 0000000000..33e8ea0073 --- /dev/null +++ b/interface/src/renderer/VoxelShader.h @@ -0,0 +1,46 @@ +// +// VoxelShader.h +// interface +// +// Created by Brad Hefta-Gaub on 9/23/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__VoxelShader__ +#define __interface__VoxelShader__ + +#include + +class ProgramObject; + +/// A generic full screen glow effect. +class VoxelShader : public QObject { + Q_OBJECT + +public: + VoxelShader(); + ~VoxelShader(); + + void init(); + + /// Starts using the voxel geometry shader effect. + void begin(); + + /// Stops using the voxel geometry shader effect. + void end(); + + /// Gets access to attributes from the shader program + int attributeLocation(const char * name) const; + + static ProgramObject* createGeometryShaderProgram(const QString& name); + +public slots: + +private: + + bool _initialized; + + ProgramObject* _program; +}; + +#endif /* defined(__interface__VoxelShader__) */ diff --git a/libraries/voxels/src/VoxelConstants.h b/libraries/voxels/src/VoxelConstants.h index 08fdbefee3..391be29282 100644 --- a/libraries/voxels/src/VoxelConstants.h +++ b/libraries/voxels/src/VoxelConstants.h @@ -32,10 +32,10 @@ const float VOXEL_SIZE_SCALE = TREE_SCALE * 400.0f; const int NUMBER_OF_CHILDREN = 8; const int MAX_VOXEL_PACKET_SIZE = 1492; const int MAX_TREE_SLICE_BYTES = 26; -const int MAX_VOXELS_PER_SYSTEM = 200000; -const int VERTICES_PER_VOXEL = 24; -const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; -const int INDICES_PER_VOXEL = 3 * 12; +const int DEFAULT_MAX_VOXELS_PER_SYSTEM = 200000; +const int VERTICES_PER_VOXEL = 24; // 6 sides * 4 corners per side +const int VERTEX_POINTS_PER_VOXEL = 3 * VERTICES_PER_VOXEL; // xyz for each VERTICE_PER_VOXEL +const int INDICES_PER_VOXEL = 3 * 12; // 6 sides * 2 triangles per size * 3 vertices per triangle const int COLOR_VALUES_PER_VOXEL = NUMBER_OF_COLORS * VERTICES_PER_VOXEL; typedef unsigned long int glBufferIndex;