diff --git a/interface/resources/scripts/sphere.js b/interface/resources/scripts/sphere.js index 403374e812..b696021fe8 100644 --- a/interface/resources/scripts/sphere.js +++ b/interface/resources/scripts/sphere.js @@ -35,11 +35,11 @@ function setNormal(vector) { if (normalIndex != -1) { var length = Math.sqrt(lengthSquared(vector[0], vector[1], vector[2])); if (length == 0.0) { - info.attributeValues[normalIndex] = 0x007F00; + info.inputValues[normalIndex] = 0x007F00; } else { var scale = 127.0 / length; - info.attributeValues[normalIndex] = + info.inputValues[normalIndex] = (Math.floor(vector[0] * scale) & 0xFF) << 16 | (Math.floor(vector[1] * scale) & 0xFF) << 8 | Math.floor(vector[2] * scale) & 0xFF; @@ -61,7 +61,7 @@ function guide(minimum, size, depth) { maximum[2] <= sphereCenter[2] - sphereRadius) { info.isLeaf = true; if (colorIndex != -1) { - info.attributeValues[colorIndex] = 0x0; + info.inputValues[colorIndex] = 0x0; } visitor.visit(info); return; @@ -110,7 +110,7 @@ function guide(minimum, size, depth) { if (inside == 8) { info.isLeaf = true; if (colorIndex != -1) { - info.attributeValues[colorIndex] = sphereColor; + info.inputValues[colorIndex] = sphereColor; } setNormal(vector); visitor.visit(info); @@ -122,13 +122,13 @@ function guide(minimum, size, depth) { info.isLeaf = true; if (inside >= 3) { if (colorIndex != -1) { - info.attributeValues[colorIndex] = sphereColor; + info.inputValues[colorIndex] = sphereColor; } setNormal(vector); } else { if (colorIndex != -1) { - info.attributeValues[colorIndex] = 0x0; + info.inputValues[colorIndex] = 0x0; } } visitor.visit(info); @@ -152,11 +152,11 @@ function guide(minimum, size, depth) { } (function(visitation) { - var attributes = visitation.visitor.getAttributes(); - colorIndex = strictIndexOf(attributes, AttributeRegistry.colorAttribute); - normalIndex = strictIndexOf(attributes, AttributeRegistry.normalAttribute); + var inputs = visitation.visitor.getInputs(); + colorIndex = strictIndexOf(inputs, AttributeRegistry.colorAttribute); + normalIndex = strictIndexOf(inputs, AttributeRegistry.normalAttribute); visitor = visitation.visitor; - info = { attributeValues: new Array(attributes.length) }; + info = { inputValues: new Array(inputs.length) }; // have the sphere orbit the center and pulse in size var time = new Date().getTime(); diff --git a/interface/resources/shaders/grid.frag b/interface/resources/shaders/grid.frag new file mode 100644 index 0000000000..b9e3baccd4 --- /dev/null +++ b/interface/resources/shaders/grid.frag @@ -0,0 +1,15 @@ +#version 120 + +// +// grid.frag +// fragment shader +// +// Created by Andrzej Kapolka on 1/21/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +void main(void) { + // use the standard exponential fog calculation + const float FOG_DENSITY = 0.5; + gl_FragColor = vec4(gl_Color.rgb, exp(-FOG_DENSITY / gl_FragCoord.w)); +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b1592115fe..2f545113bd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1152,13 +1152,12 @@ void Application::mouseMoveEvent(QMouseEvent* event) { _seenMouseMove = true; } + int deltaX = event->x() - _mouseX; + int deltaY = event->y() - _mouseY; + _mouseX = event->x(); + _mouseY = event->y(); + if (activeWindow() == _window) { - int deltaX = event->x() - _mouseX; - int deltaY = event->y() - _mouseY; - - _mouseX = event->x(); - _mouseY = event->y(); - // orbit behavior if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) { if (_lookatTargetAvatar) { @@ -1849,25 +1848,23 @@ const float HEAD_SPHERE_RADIUS = 0.07f; static QUuid DEFAULT_NODE_ID_REF; -void Application::updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition) { +void Application::updateLookatTargetAvatar(glm::vec3& eyePosition) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); if (!_mousePressed) { - _lookatTargetAvatar = findLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, eyePosition, DEFAULT_NODE_ID_REF); + _lookatTargetAvatar = findLookatTargetAvatar(eyePosition, DEFAULT_NODE_ID_REF); } } -Avatar* Application::findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) { +Avatar* Application::findLookatTargetAvatar(glm::vec3& eyePosition, QUuid& nodeUUID = DEFAULT_NODE_ID_REF) { foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { Avatar* avatar = (Avatar*)node->getLinkedData(); float distance; - if (avatar->findRayIntersection(mouseRayOrigin, mouseRayDirection, distance)) { + if (avatar->findRayIntersection(_mouseRayOrigin, _mouseRayDirection, distance)) { // rescale to compensate for head embiggening eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); @@ -1918,7 +1915,7 @@ void Application::renderHighlightVoxel(VoxelDetail voxel) { glPopMatrix(); } -void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection) { +void Application::updateAvatars(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); @@ -1930,7 +1927,7 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: avatar->init(); } avatar->simulate(deltaTime, NULL); - avatar->setMouseRay(mouseRayOrigin, mouseRayDirection); + avatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection); } } @@ -1953,28 +1950,28 @@ void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm:: } } -void Application::updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection) { +void Application::updateMouseRay() { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseRay()"); _viewFrustum.computePickRay(_mouseX / (float)_glWidget->width(), _mouseY / (float)_glWidget->height(), - mouseRayOrigin, mouseRayDirection); + _mouseRayOrigin, _mouseRayDirection); // adjust for mirroring if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - glm::vec3 mouseRayOffset = mouseRayOrigin - _viewFrustum.getPosition(); - mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) + + glm::vec3 mouseRayOffset = _mouseRayOrigin - _viewFrustum.getPosition(); + _mouseRayOrigin -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayOffset) + _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayOffset)); - mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), mouseRayDirection) + - _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), mouseRayDirection)); + _mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) + + _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), _mouseRayDirection)); } // tell my avatar if the mouse is being pressed... _myAvatar.setMousePressed(_mousePressed); // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position - _myAvatar.setMouseRay(mouseRayOrigin, mouseRayDirection); + _myAvatar.setMouseRay(_mouseRayOrigin, _mouseRayDirection); } void Application::updateFaceshift() { @@ -1991,8 +1988,7 @@ void Application::updateFaceshift() { } } -void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, - glm::vec3& lookAtRayDirection) { +void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); @@ -2009,7 +2005,7 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& } else { // just look in direction of the mouse ray - lookAtSpot = lookAtRayOrigin + lookAtRayDirection * FAR_AWAY_STARE; + lookAtSpot = _mouseRayOrigin + _mouseRayDirection * FAR_AWAY_STARE; } if (_faceshift.isActive()) { // deflect using Faceshift gaze data @@ -2023,8 +2019,7 @@ void Application::updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& _myAvatar.getHead().setLookAtPosition(lookAtSpot); } -void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face) { +void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& face) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels()"); @@ -2055,7 +2050,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, if (!(_voxels.treeIsBusy() || _mousePressed)) { { PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()"); - _isHoverVoxel = _voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _hoverVoxel, distance, face); + _isHoverVoxel = _voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _hoverVoxel, distance, face); } if (MAKE_SOUND_ON_VOXEL_HOVER && _isHoverVoxel && glm::vec4(_hoverVoxel.x, _hoverVoxel.y, _hoverVoxel.z, _hoverVoxel.s) != oldVoxel) { @@ -2071,8 +2066,7 @@ void Application::updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, } } -void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face) { +void Application::updateMouseVoxels(float deltaTime, float& distance, BoxFace& face) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMouseVoxels()"); @@ -2084,7 +2078,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, fabs(_myAvatar.getVelocity().y) + fabs(_myAvatar.getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) { - if (_voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _mouseVoxel, distance, face)) { + if (_voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _mouseVoxel, distance, face)) { if (distance < MAX_VOXEL_EDIT_DISTANCE) { // set the voxel scale to that of the first moused-over voxel if (!wasInitialized) { @@ -2104,7 +2098,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3 faceVector = getFaceVector(face); if (_mouseVoxelScale < _mouseVoxel.s) { // find the closest contained voxel - glm::vec3 pt = (mouseRayOrigin + mouseRayDirection * distance) / (float)TREE_SCALE - + glm::vec3 pt = (_mouseRayOrigin + _mouseRayDirection * distance) / (float)TREE_SCALE - faceVector * (_mouseVoxelScale * 0.5f); _mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / _mouseVoxelScale); _mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / _mouseVoxelScale); @@ -2125,7 +2119,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, || Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { // place the voxel a fixed distance away float worldMouseVoxelScale = _mouseVoxelScale * TREE_SCALE; - glm::vec3 pt = mouseRayOrigin + mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f); + glm::vec3 pt = _mouseRayOrigin + _mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f); _mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / worldMouseVoxelScale); _mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / worldMouseVoxelScale); _mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / worldMouseVoxelScale); @@ -2359,29 +2353,28 @@ void Application::update(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::update()"); // check what's under the mouse and update the mouse voxel - glm::vec3 mouseRayOrigin, mouseRayDirection; - updateMouseRay(deltaTime, mouseRayOrigin, mouseRayDirection); + updateMouseRay(); // Set where I am looking based on my mouse ray (so that other people can see) glm::vec3 lookAtSpot; updateFaceshift(); - updateLookatTargetAvatar(mouseRayOrigin, mouseRayDirection, lookAtSpot); - updateMyAvatarLookAtPosition(lookAtSpot, mouseRayOrigin, mouseRayDirection); + updateLookatTargetAvatar(lookAtSpot); + updateMyAvatarLookAtPosition(lookAtSpot); // Find the voxel we are hovering over, and respond if clicked float distance; BoxFace face; - updateHoverVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // clicking on voxels and making sounds - updateMouseVoxels(deltaTime, mouseRayOrigin, mouseRayDirection, distance, face); // UI/UX related to voxels + updateHoverVoxels(deltaTime, distance, face); // clicking on voxels and making sounds + updateMouseVoxels(deltaTime, distance, face); // UI/UX related to voxels updateHandAndTouch(deltaTime); // Update state for touch sensors updateLeap(deltaTime); // Leap finger-sensing device updateSixense(deltaTime); // Razer Hydra controllers updateSerialDevices(deltaTime); // Read serial port interface devices updateAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... - updateAvatars(deltaTime, mouseRayOrigin, mouseRayDirection); //loop through all the other avatars and simulate them... + updateAvatars(deltaTime); //loop through all the other avatars and simulate them... updateMyAvatarSimulation(deltaTime); // Simulate myself updateParticles(deltaTime); // Simulate particle cloud movements updateMetavoxels(deltaTime); // update metavoxels @@ -3030,6 +3023,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glPopMatrix(); } + + // give external parties a change to hook in + emit renderingInWorldInterface(); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index db3ded55be..203d74fa82 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -147,10 +147,13 @@ public: ViewFrustum* getViewFrustum() { return &_viewFrustum; } VoxelSystem* getVoxels() { return &_voxels; } ParticleTreeRenderer* getParticles() { return &_particles; } + MetavoxelSystem* getMetavoxels() { return &_metavoxels; } VoxelSystem* getSharedVoxelSystem() { return &_sharedVoxelSystem; } VoxelTree* getClipboard() { return &_clipboard; } Environment* getEnvironment() { return &_environment; } bool isMouseHidden() const { return _mouseHidden; } + const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } + const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } Faceshift* getFaceshift() { return &_faceshift; } SixenseManager* getSixenseManager() { return &_sixenseManager; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } @@ -203,6 +206,11 @@ public: void skipVersion(QString latestVersion); +signals: + + /// Fired when we're rendering in-world interface elements; allows external parties to hook in. + void renderingInWorldInterface(); + public slots: void domainChanged(const QString& domainHostname); void nodeKilled(SharedNodePointer node); @@ -266,15 +274,12 @@ private: void update(float deltaTime); // Various helper functions called during update() - void updateMouseRay(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection); + void updateMouseRay(); void updateFaceshift(); - void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot, glm::vec3& lookAtRayOrigin, glm::vec3& lookAtRayDirection); - void updateHoverVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face); - void updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, glm::vec3& mouseRayDirection, - float& distance, BoxFace& face); - void updateLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition); + void updateMyAvatarLookAtPosition(glm::vec3& lookAtSpot); + void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face); + void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face); + void updateLookatTargetAvatar(glm::vec3& eyePosition); void updateHandAndTouch(float deltaTime); void updateLeap(float deltaTime); void updateSixense(float deltaTime); @@ -289,15 +294,14 @@ private: void updateAudio(float deltaTime); void updateCursor(float deltaTime); - Avatar* findLookatTargetAvatar(const glm::vec3& mouseRayOrigin, const glm::vec3& mouseRayDirection, - glm::vec3& eyePosition, QUuid &nodeUUID); + Avatar* findLookatTargetAvatar(glm::vec3& eyePosition, QUuid &nodeUUID); bool isLookingAtMyAvatar(Avatar* avatar); void renderLookatIndicator(glm::vec3 pointOfInterest); void renderHighlightVoxel(VoxelDetail voxel); void updateAvatar(float deltaTime); - void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection); + void updateAvatars(float deltaTime); void queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, NodeToJurisdictionMap& jurisdictions); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); @@ -402,6 +406,9 @@ private: bool _mouseHidden; bool _seenMouseMove; + glm::vec3 _mouseRayOrigin; + glm::vec3 _mouseRayDirection; + float _touchAvgX; float _touchAvgY; float _lastTouchAvgX; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 55f721afea..c717957da7 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -32,6 +32,7 @@ #include "Menu.h" #include "Util.h" #include "InfoView.h" +#include "ui/MetavoxelEditor.h" Menu* Menu::_instance = NULL; @@ -220,6 +221,8 @@ Menu::Menu() : SLOT(increaseVoxelSize())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors())); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor())); + QMenu* viewMenu = addMenu("View"); @@ -1010,6 +1013,13 @@ void Menu::bandwidthDetails() { _bandwidthDialog->raise(); } +void Menu::showMetavoxelEditor() { + if (!_MetavoxelEditor) { + _MetavoxelEditor = new MetavoxelEditor(); + } + _MetavoxelEditor->raise(); +} + void Menu::audioMuteToggled() { QAction *muteAction = _actionHash.value(MenuOption::MuteAudio); muteAction->setChecked(Application::getInstance()->getAudio()->getMuted()); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index dd3d5b7588..3f597bb4ad 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -36,8 +37,9 @@ struct ViewFrustumOffset { class QSettings; class BandwidthDialog; -class VoxelStatsDialog; class LodToolsDialog; +class MetavoxelEditor; +class VoxelStatsDialog; class Menu : public QMenuBar, public AbstractMenuInterface { Q_OBJECT @@ -107,6 +109,7 @@ private slots: void chooseVoxelPaintColor(); void runTests(); void resetSwatchColors(); + void showMetavoxelEditor(); void audioMuteToggled(); private: @@ -140,6 +143,7 @@ private: FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; QActionGroup* _voxelModeActionsGroup; + QPointer _MetavoxelEditor; VoxelStatsDialog* _voxelStatsDialog; LodToolsDialog* _lodToolsDialog; int _maxVoxels; @@ -219,6 +223,7 @@ namespace MenuOption { const QString Login = "Login"; const QString LookAtIndicator = "Look-at Indicator"; const QString LookAtVectors = "Look-at Vectors"; + const QString MetavoxelEditor = "Metavoxel Editor..."; const QString Metavoxels = "Metavoxels"; const QString Mirror = "Mirror"; const QString MoveWithLean = "Move with Lean"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index fc72f7444e..258db6da00 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -6,8 +6,6 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // -#include -#include #include #include @@ -40,14 +38,6 @@ void MetavoxelSystem::init() { connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); - AttributeRegistry::getInstance()->configureScriptEngine(&_scriptEngine); - - QFile scriptFile("resources/scripts/sphere.js"); - scriptFile.open(QIODevice::ReadOnly); - QScriptValue guideFunction = _scriptEngine.evaluate(QTextStream(&scriptFile).readAll()); - _data.setAttributeValue(MetavoxelPath(), AttributeValue(AttributeRegistry::getInstance()->getGuideAttribute(), - encodeInline(PolymorphicDataPointer(new ScriptedMetavoxelGuide(guideFunction))))); - _buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); _buffer.create(); } @@ -156,16 +146,17 @@ void MetavoxelSystem::receivedData(const QByteArray& data, const HifiSockAddr& s MetavoxelSystem::PointVisitor::PointVisitor(QVector& points) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getColorAttribute() << - AttributeRegistry::getInstance()->getNormalAttribute()), + AttributeRegistry::getInstance()->getNormalAttribute(), + QVector()), _points(points) { } -bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) { +bool MetavoxelSystem::PointVisitor::visit(MetavoxelInfo& info) { if (!info.isLeaf) { return true; } - QRgb color = info.attributeValues.at(0).getInlineValue(); - QRgb normal = info.attributeValues.at(1).getInlineValue(); + QRgb color = info.inputValues.at(0).getInlineValue(); + QRgb normal = info.inputValues.at(1).getInlineValue(); int alpha = qAlpha(color); if (alpha > 0) { Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index a58729285d..708b4d0839 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -30,18 +29,23 @@ class MetavoxelSystem : public QObject { Q_OBJECT public: + MetavoxelSystem(); void init(); + MetavoxelData& getData() { return _data; } + void processData(const QByteArray& data, const HifiSockAddr& sender); void simulate(float deltaTime); void render(); public slots: + void nodeAdded(SharedNodePointer node); void nodeKilled(SharedNodePointer node); + private: Q_INVOKABLE void addClient(const QUuid& uuid, const HifiSockAddr& address); @@ -58,7 +62,7 @@ private: class PointVisitor : public MetavoxelVisitor { public: PointVisitor(QVector& points); - virtual bool visit(const MetavoxelInfo& info); + virtual bool visit(MetavoxelInfo& info); private: QVector& _points; @@ -67,7 +71,6 @@ private: static ProgramObject _program; static int _pointScaleLocation; - QScriptEngine _scriptEngine; MetavoxelData _data; QVector _points; PointVisitor _pointVisitor; diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 9fff306aca..bdb3916694 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -7,7 +7,11 @@ #include +// include this before QOpenGLBuffer, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + #include +#include #include "Application.h" #include "GeometryCache.h" @@ -241,6 +245,50 @@ void GeometryCache::renderHalfCylinder(int slices, int stacks) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } +void GeometryCache::renderGrid(int xDivisions, int yDivisions) { + QOpenGLBuffer& buffer = _gridBuffers[IntPair(xDivisions, yDivisions)]; + int vertices = (xDivisions + 1 + yDivisions + 1) * 2; + if (!buffer.isCreated()) { + GLfloat* vertexData = new GLfloat[vertices * 2]; + GLfloat* vertex = vertexData; + for (int i = 0; i <= xDivisions; i++) { + float x = (float)i / xDivisions; + + *(vertex++) = x; + *(vertex++) = 0.0f; + + *(vertex++) = x; + *(vertex++) = 1.0f; + } + for (int i = 0; i <= yDivisions; i++) { + float y = (float)i / yDivisions; + + *(vertex++) = 0.0f; + *(vertex++) = y; + + *(vertex++) = 1.0f; + *(vertex++) = y; + } + buffer.create(); + buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); + buffer.bind(); + buffer.allocate(vertexData, vertices * 2 * sizeof(GLfloat)); + delete[] vertexData; + + } else { + buffer.bind(); + } + glEnableClientState(GL_VERTEX_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, 0); + + glDrawArrays(GL_LINES, 0, vertices); + + glDisableClientState(GL_VERTEX_ARRAY); + + buffer.release(); +} + QSharedPointer GeometryCache::getGeometry(const QUrl& url) { QSharedPointer geometry = _networkGeometry.value(url); if (geometry.isNull()) { diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 8a68917ba5..312d8bcd91 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -19,6 +19,7 @@ #include "InterfaceConfig.h" class QNetworkReply; +class QOpenGLBuffer; class NetworkGeometry; class NetworkMesh; @@ -33,6 +34,7 @@ public: void renderHemisphere(int slices, int stacks); void renderSquare(int xDivisions, int yDivisions); void renderHalfCylinder(int slices, int stacks); + void renderGrid(int xDivisions, int yDivisions); /// Loads geometry from the specified URL. QSharedPointer getGeometry(const QUrl& url); @@ -45,6 +47,7 @@ private: QHash _hemisphereVBOs; QHash _squareVBOs; QHash _halfCylinderVBOs; + QHash _gridBuffers; QHash > _networkGeometry; }; diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp new file mode 100644 index 0000000000..d3e4494ac9 --- /dev/null +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -0,0 +1,405 @@ +// +// MetavoxelEditor.cpp +// interface +// +// Created by Andrzej Kapolka on 1/21/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Application.h" +#include "MetavoxelEditor.h" + +enum GridPlane { + GRID_PLANE_XY, GRID_PLANE_XZ, GRID_PLANE_YZ +}; + +const glm::vec2 INVALID_VECTOR(FLT_MAX, FLT_MAX); + +MetavoxelEditor::MetavoxelEditor() : + QDialog(Application::getInstance()->getGLWidget()) { + + setWindowTitle("Metavoxel Editor"); + setAttribute(Qt::WA_DeleteOnClose); + + QVBoxLayout* topLayout = new QVBoxLayout(); + setLayout(topLayout); + + QGroupBox* attributeGroup = new QGroupBox(); + attributeGroup->setTitle("Attributes"); + topLayout->addWidget(attributeGroup); + + QVBoxLayout* attributeLayout = new QVBoxLayout(); + attributeGroup->setLayout(attributeLayout); + + attributeLayout->addWidget(_attributes = new QListWidget()); + connect(_attributes, SIGNAL(itemSelectionChanged()), SLOT(updateValueEditor())); + + QPushButton* newAttribute = new QPushButton("New..."); + attributeLayout->addWidget(newAttribute); + connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute())); + + QFormLayout* formLayout = new QFormLayout(); + topLayout->addLayout(formLayout); + + formLayout->addRow("Grid Plane:", _gridPlane = new QComboBox()); + _gridPlane->addItem("X/Y"); + _gridPlane->addItem("X/Z"); + _gridPlane->addItem("Y/Z"); + _gridPlane->setCurrentIndex(GRID_PLANE_XZ); + + formLayout->addRow("Grid Spacing:", _gridSpacing = new QDoubleSpinBox()); + _gridSpacing->setValue(0.1); + _gridSpacing->setMaximum(FLT_MAX); + _gridSpacing->setSingleStep(0.01); + connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(updateGridPosition())); + + formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox()); + _gridPosition->setSingleStep(0.1); + _gridPosition->setMinimum(-FLT_MAX); + _gridPosition->setMaximum(FLT_MAX); + + _value = new QGroupBox(); + _value->setTitle("Value"); + topLayout->addWidget(_value); + + QVBoxLayout* valueLayout = new QVBoxLayout(); + _value->setLayout(valueLayout); + + updateAttributes(); + + connect(Application::getInstance(), SIGNAL(renderingInWorldInterface()), SLOT(render())); + + Application::getInstance()->getGLWidget()->installEventFilter(this); + + resetState(); + + show(); + + if (_gridProgram.isLinked()) { + return; + } + switchToResourcesParentIfRequired(); + _gridProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/grid.frag"); + _gridProgram.link(); +} + +bool MetavoxelEditor::eventFilter(QObject* watched, QEvent* event) { + switch (_state) { + case HOVERING_STATE: + if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) { + _state = DRAGGING_STATE; + return true; + } + break; + + case DRAGGING_STATE: + if (event->type() == QEvent::MouseButtonRelease) { + _state = RAISING_STATE; + return true; + } + break; + + case RAISING_STATE: + if (event->type() == QEvent::MouseButtonPress) { + if (_height != 0) { + // find the start and end corners in X/Y + float base = _gridPosition->value(); + float top = base + _height; + glm::quat rotation = getGridRotation(); + glm::vec3 start = rotation * glm::vec3(glm::min(_startPosition, _endPosition), glm::min(base, top)); + float spacing = _gridSpacing->value(); + glm::vec3 end = rotation * glm::vec3(glm::max(_startPosition, _endPosition) + + glm::vec2(spacing, spacing), glm::max(base, top)); + + // find the minimum and maximum extents after rotation + applyValue(glm::min(start, end), glm::max(start, end)); + } + resetState(); + return true; + } + break; + } + return false; +} + +void MetavoxelEditor::updateValueEditor() { + QString selected = getSelectedAttribute(); + if (selected.isNull()) { + _value->setVisible(false); + return; + } + _value->setVisible(true); + + if (!_value->layout()->isEmpty()) { + delete _value->layout()->takeAt(0); + } + + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); + QWidget* editor = attribute->createEditor(); + if (editor) { + _value->layout()->addWidget(editor); + } +} + +void MetavoxelEditor::createNewAttribute() { + QDialog dialog(this); + dialog.setWindowTitle("New Attribute"); + + QVBoxLayout layout; + dialog.setLayout(&layout); + + QFormLayout form; + layout.addLayout(&form); + + QLineEdit name; + form.addRow("Name:", &name); + + QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + dialog.connect(&buttons, SIGNAL(accepted()), SLOT(accept())); + dialog.connect(&buttons, SIGNAL(rejected()), SLOT(reject())); + + layout.addWidget(&buttons); + + if (!dialog.exec()) { + return; + } + QString nameText = name.text().trimmed(); + AttributeRegistry::getInstance()->registerAttribute(new QRgbAttribute(nameText)); + + updateAttributes(nameText); +} + +void MetavoxelEditor::updateGridPosition() { + // make sure our grid position matches our grid spacing + double step = _gridSpacing->value(); + if (step > 0.0) { + _gridPosition->setSingleStep(step); + _gridPosition->setValue(step * floor(_gridPosition->value() / step)); + } +} + +void MetavoxelEditor::render() { + QString selected = getSelectedAttribute(); + if (selected.isNull()) { + resetState(); + return; + } + + glDisable(GL_LIGHTING); + glDepthMask(GL_FALSE); + + glPushMatrix(); + + glm::quat rotation = getGridRotation(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); + + glm::quat inverseRotation = glm::inverse(rotation); + glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin(); + glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection(); + float spacing = _gridSpacing->value(); + float position = _gridPosition->value(); + if (_state == RAISING_STATE) { + // find the plane at the mouse position, orthogonal to the plane, facing the eye position + glLineWidth(4.0f); + glm::vec3 eyePosition = inverseRotation * Application::getInstance()->getViewFrustum()->getOffsetPosition(); + glm::vec3 mousePoint = glm::vec3(_mousePosition, position); + glm::vec3 right = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), eyePosition - mousePoint); + glm::vec3 normal = glm::cross(right, glm::vec3(0.0f, 0.0f, 1.0f)); + float divisor = glm::dot(normal, rayDirection); + if (fabs(divisor) > EPSILON) { + float distance = (glm::dot(normal, mousePoint) - glm::dot(normal, rayOrigin)) / divisor; + float projection = rayOrigin.z + distance * rayDirection.z; + _height = spacing * roundf(projection / spacing) - position; + } + } else if (fabs(rayDirection.z) > EPSILON) { + // find the intersection of the rotated mouse ray with the plane + float distance = (position - rayOrigin.z) / rayDirection.z; + _mousePosition = glm::vec2(rayOrigin + rayDirection * distance); + glm::vec2 snappedPosition = spacing * glm::floor(_mousePosition / spacing); + + if (_state == HOVERING_STATE) { + _startPosition = _endPosition = snappedPosition; + glLineWidth(2.0f); + + } else if (_state == DRAGGING_STATE) { + _endPosition = snappedPosition; + glLineWidth(4.0f); + } + } else { + // cancel any operation in progress + resetState(); + } + + const float GRID_BRIGHTNESS = 0.5f; + if (_startPosition != INVALID_VECTOR) { + glm::vec2 minimum = glm::min(_startPosition, _endPosition); + glm::vec2 maximum = glm::max(_startPosition, _endPosition); + + glPushMatrix(); + glTranslatef(minimum.x, minimum.y, position); + glScalef(maximum.x + spacing - minimum.x, maximum.y + spacing - minimum.y, _height); + + glTranslatef(0.5f, 0.5f, 0.5f); + if (_state != HOVERING_STATE) { + const float BOX_ALPHA = 0.25f; + QColor color = getValue().value(); + if (color.isValid()) { + glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA); + } else { + glColor4f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS, BOX_ALPHA); + } + glEnable(GL_CULL_FACE); + glutSolidCube(1.0); + glDisable(GL_CULL_FACE); + } + glutWireCube(1.0); + + glPopMatrix(); + } + + glLineWidth(1.0f); + + // center the grid around the camera position on the plane + glm::vec3 rotated = inverseRotation * Application::getInstance()->getCamera()->getPosition(); + const int GRID_DIVISIONS = 300; + glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position); + + float scale = GRID_DIVISIONS * spacing; + glScalef(scale, scale, scale); + + _gridProgram.bind(); + + glColor3f(GRID_BRIGHTNESS, GRID_BRIGHTNESS, GRID_BRIGHTNESS); + Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS); + + _gridProgram.release(); + + glPopMatrix(); + + glEnable(GL_LIGHTING); + glDepthMask(GL_TRUE); +} + +void MetavoxelEditor::updateAttributes(const QString& select) { + // remember the selection in order to preserve it + QString selected = select.isNull() ? getSelectedAttribute() : select; + _attributes->clear(); + + // sort the names for consistent ordering + QList names = AttributeRegistry::getInstance()->getAttributes().keys(); + qSort(names); + + foreach (const QString& name, names) { + QListWidgetItem* item = new QListWidgetItem(name); + _attributes->addItem(item); + if (name == selected || selected.isNull()) { + item->setSelected(true); + selected = name; + } + } +} + +QString MetavoxelEditor::getSelectedAttribute() const { + QList selectedItems = _attributes->selectedItems(); + return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); +} + +glm::quat MetavoxelEditor::getGridRotation() const { + // for simplicity, we handle the other two planes by rotating them onto X/Y and performing computation there + switch (_gridPlane->currentIndex()) { + case GRID_PLANE_XY: + return glm::quat(); + + case GRID_PLANE_XZ: + return glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f); + + case GRID_PLANE_YZ: + return glm::angleAxis(90.0f, 0.0f, 1.0f, 0.0f); + } +} + +void MetavoxelEditor::resetState() { + _state = HOVERING_STATE; + _startPosition = INVALID_VECTOR; + _height = 0.0f; +} + +class Applier : public MetavoxelVisitor { +public: + + Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value); + + virtual bool visit(MetavoxelInfo& info); + +protected: + + glm::vec3 _minimum; + glm::vec3 _maximum; + float _granularity; + AttributeValue _value; +}; + +Applier::Applier(const glm::vec3& minimum, const glm::vec3& maximum, float granularity, const AttributeValue& value) : + MetavoxelVisitor(QVector(), QVector() << value.getAttribute()), + _minimum(minimum), + _maximum(maximum), + _granularity(granularity), + _value(value) { +} + +bool Applier::visit(MetavoxelInfo& info) { + // find the intersection between volume and voxel + glm::vec3 minimum = glm::max(info.minimum, _minimum); + glm::vec3 maximum = glm::min(info.minimum + glm::vec3(info.size, info.size, info.size), _maximum); + glm::vec3 size = maximum - minimum; + if (size.x <= 0.0f || size.y <= 0.0f || size.z <= 0.0f) { + return false; // disjoint + } + float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size); + if (volume >= 1.0f) { + info.outputValues[0] = _value; + return false; // entirely contained + } + if (info.size <= _granularity) { + if (volume > 0.5f) { + info.outputValues[0] = _value; + } + return false; // reached granularity limit; take best guess + } + return true; // subdivide +} + +void MetavoxelEditor::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) { + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(getSelectedAttribute()); + if (!attribute) { + return; + } + OwnedAttributeValue value(attribute, attribute->createFromVariant(getValue())); + + Applier applier(minimum, maximum, _gridSpacing->value(), value); + Application::getInstance()->getMetavoxels()->getData().guide(applier); +} + +QVariant MetavoxelEditor::getValue() const { + if (_value->layout()->isEmpty()) { + return QVariant(); + } + QWidget* editor = _value->layout()->itemAt(0)->widget(); + return editor->metaObject()->userProperty().read(editor); +} + +ProgramObject MetavoxelEditor::_gridProgram; diff --git a/interface/src/ui/MetavoxelEditor.h b/interface/src/ui/MetavoxelEditor.h new file mode 100644 index 0000000000..21c8478d95 --- /dev/null +++ b/interface/src/ui/MetavoxelEditor.h @@ -0,0 +1,67 @@ +// +// MetavoxelEditor.h +// interface +// +// Created by Andrzej Kapolka on 1/21/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__MetavoxelEditor__ +#define __interface__MetavoxelEditor__ + +#include + +#include "renderer/ProgramObject.h" + +class QComboBox; +class QDoubleSpinBox; +class QGroupBox; +class QListWidget; + +/// Allows editing metavoxels. +class MetavoxelEditor : public QDialog { + Q_OBJECT + +public: + + MetavoxelEditor(); + + virtual bool eventFilter(QObject* watched, QEvent* event); + +private slots: + + void updateValueEditor(); + void createNewAttribute(); + void updateGridPosition(); + + void render(); + +private: + + void updateAttributes(const QString& select = QString()); + QString getSelectedAttribute() const; + glm::quat getGridRotation() const; + void resetState(); + void applyValue(const glm::vec3& minimum, const glm::vec3& maximum); + QVariant getValue() const; + + QListWidget* _attributes; + QComboBox* _gridPlane; + QDoubleSpinBox* _gridSpacing; + QDoubleSpinBox* _gridPosition; + QGroupBox* _value; + + enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE }; + + State _state; + + glm::vec2 _mousePosition; ///< the position of the mouse in rotated space + + glm::vec2 _startPosition; ///< the first corner of the selection base + glm::vec2 _endPosition; ///< the second corner of the selection base + float _height; ///< the selection height + + static ProgramObject _gridProgram; +}; + +#endif /* defined(__interface__MetavoxelEditor__) */ diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9a1f220034..5b7a8859ca 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -6,7 +6,10 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include +#include #include +#include #include "AttributeRegistry.h" #include "MetavoxelData.h" @@ -69,12 +72,12 @@ bool AttributeValue::operator==(void* other) const { return _attribute && _attribute->equal(_value, other); } -OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) : - AttributeValue(attribute, attribute ? attribute->create() : NULL) { +OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) : + AttributeValue(attribute, value) { } -OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute, void* value) : - AttributeValue(attribute, attribute ? attribute->create(value) : NULL) { +OwnedAttributeValue::OwnedAttributeValue(const AttributePointer& attribute) : + AttributeValue(attribute, attribute ? attribute->create() : NULL) { } OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) : @@ -92,7 +95,7 @@ OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) _attribute->destroy(_value); } if ((_attribute = other.getAttribute())) { - _value = _attribute->create(other.getValue()); + _value = other.copy(); } return *this; } @@ -132,6 +135,41 @@ void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* return encodeInline((QRgb)value.toUInt32()); } +void* QRgbAttribute::createFromVariant(const QVariant& value) const { + switch (value.userType()) { + case QMetaType::QColor: + return encodeInline(value.value().rgba()); + + default: + return encodeInline((QRgb)value.toUInt()); + } +} + +QWidget* QRgbAttribute::createEditor(QWidget* parent) const { + QRgbEditor* editor = new QRgbEditor(parent); + editor->setColor(QColor::fromRgba(_defaultValue)); + return editor; +} + +QRgbEditor::QRgbEditor(QWidget* parent) : QWidget(parent) { + setLayout(new QVBoxLayout()); + layout()->addWidget(_button = new QPushButton()); + connect(_button, SIGNAL(clicked()), SLOT(selectColor())); +} + +void QRgbEditor::setColor(const QColor& color) { + QString name = (_color = color).name(); + _button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color.rgb()).name())); + _button->setText(name); +} + +void QRgbEditor::selectColor() { + QColor color = QColorDialog::getColor(_color, this, QString(), QColorDialog::ShowAlphaChannel); + if (color.isValid()) { + setColor(color); + } +} + PolymorphicData::~PolymorphicData() { } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 4f2f1d79b2..058b02d78f 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -16,9 +16,11 @@ #include #include #include +#include #include "Bitstream.h" +class QPushButton; class QScriptContext; class QScriptEngine; class QScriptValue; @@ -52,6 +54,9 @@ public: /// Retrieves an attribute by name. AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); } + /// Returns a reference to the attribute hash. + const QHash& getAttributes() const { return _attributes; } + /// Returns a reference to the standard PolymorphicDataPointer "guide" attribute. const AttributePointer& getGuideAttribute() const { return _guideAttribute; } @@ -113,11 +118,19 @@ protected: class OwnedAttributeValue : public AttributeValue { public: - OwnedAttributeValue(const AttributePointer& attribute = AttributePointer()); + /// Assumes ownership of the specified value. It will be destroyed when this is destroyed or reassigned. OwnedAttributeValue(const AttributePointer& attribute, void* value); + + /// Creates an owned attribute with a copy of the specified attribute's default value. + OwnedAttributeValue(const AttributePointer& attribute = AttributePointer()); + + /// Creates an owned attribute with a copy of the specified other value. OwnedAttributeValue(const AttributeValue& other); + + /// Destroys the current value, if any. ~OwnedAttributeValue(); + /// Destroys the current value, if any, and copies the specified other value. OwnedAttributeValue& operator=(const AttributeValue& other); }; @@ -153,6 +166,12 @@ public: virtual void* getDefaultValue() const = 0; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); } + + virtual void* createFromVariant(const QVariant& value) const { return create(); } + + /// Creates a widget to use to edit values of this attribute, or returns NULL if the attribute isn't editable. + /// The widget should have a single "user" property that will be used to get/set the value. + virtual QWidget* createEditor(QWidget* parent = NULL) const { return NULL; } }; /// A simple attribute class that stores its values inline. @@ -222,6 +241,33 @@ public: virtual bool merge(void*& parent, void* children[]) const; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; + + virtual void* createFromVariant(const QVariant& value) const; + + virtual QWidget* createEditor(QWidget* parent = NULL) const; +}; + +/// Editor for RGBA values. +class QRgbEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(QColor color MEMBER _color WRITE setColor USER true) + +public: + + QRgbEditor(QWidget* parent); + +public slots: + + void setColor(const QColor& color); + +private slots: + + void selectColor(); + +private: + + QPushButton* _button; + QColor _color; }; /// An attribute class that stores pointers to its values. diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 5b7e71ca20..ffbe5b4ff8 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -32,46 +32,36 @@ MetavoxelData& MetavoxelData::operator=(const MetavoxelData& other) { void MetavoxelData::guide(MetavoxelVisitor& visitor) { // start with the root values/defaults (plus the guide attribute) const float TOP_LEVEL_SIZE = 1.0f; - const QVector& attributes = visitor.getAttributes(); - MetavoxelVisitation firstVisitation = { visitor, QVector(attributes.size() + 1), - { glm::vec3(), TOP_LEVEL_SIZE, QVector(attributes.size() + 1) } }; - for (int i = 0; i < attributes.size(); i++) { - MetavoxelNode* node = _roots.value(attributes[i]); - firstVisitation.nodes[i] = node; - firstVisitation.info.attributeValues[i] = node ? node->getAttributeValue(attributes[i]) : attributes[i]; + const QVector& inputs = visitor.getInputs(); + const QVector& outputs = visitor.getOutputs(); + MetavoxelVisitation firstVisitation = { this, NULL, visitor, QVector(inputs.size() + 1), + QVector(outputs.size()), { glm::vec3(), TOP_LEVEL_SIZE, + QVector(inputs.size() + 1), QVector(outputs.size()) } }; + for (int i = 0; i < inputs.size(); i++) { + MetavoxelNode* node = _roots.value(inputs.at(i)); + firstVisitation.inputNodes[i] = node; + firstVisitation.info.inputValues[i] = node ? node->getAttributeValue(inputs[i]) : inputs[i]; } AttributePointer guideAttribute = AttributeRegistry::getInstance()->getGuideAttribute(); MetavoxelNode* node = _roots.value(guideAttribute); - firstVisitation.nodes.last() = node; - firstVisitation.info.attributeValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; - static_cast(firstVisitation.info.attributeValues.last().getInlineValue< + firstVisitation.inputNodes.last() = node; + firstVisitation.info.inputValues.last() = node ? node->getAttributeValue(guideAttribute) : guideAttribute; + for (int i = 0; i < outputs.size(); i++) { + MetavoxelNode* node = _roots.value(outputs.at(i)); + firstVisitation.outputNodes[i] = node; + } + static_cast(firstVisitation.info.inputValues.last().getInlineValue< PolymorphicDataPointer>().data())->guide(firstVisitation); -} - -void MetavoxelData::setAttributeValue(const MetavoxelPath& path, const AttributeValue& attributeValue) { - MetavoxelNode*& node = _roots[attributeValue.getAttribute()]; - if (node == NULL) { - node = new MetavoxelNode(attributeValue.getAttribute()); - } - if (node->setAttributeValue(path, 0, attributeValue) && attributeValue.isDefault()) { - node->decrementReferenceCount(attributeValue.getAttribute()); - _roots.remove(attributeValue.getAttribute()); - } -} - -AttributeValue MetavoxelData::getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const { - MetavoxelNode* node = _roots.value(attribute); - if (node == NULL) { - return AttributeValue(attribute); - } - for (int i = 0, n = path.getSize(); i < n; i++) { - MetavoxelNode* child = node->getChild(path[i]); - if (child == NULL) { - return node->getAttributeValue(attribute); + for (int i = 0; i < outputs.size(); i++) { + AttributeValue& value = firstVisitation.info.outputValues[i]; + if (value.getAttribute()) { + MetavoxelNode* node = firstVisitation.outputNodes.at(i); + if (node->isLeaf() && value.isDefault()) { + node->decrementReferenceCount(value.getAttribute()); + _roots.remove(value.getAttribute()); + } } - node = child; } - return node->getAttributeValue(attribute); } void MetavoxelData::read(Bitstream& in) { @@ -80,7 +70,7 @@ void MetavoxelData::read(Bitstream& in) { _roots.clear(); // read in the new roots, reusing old ones where appropriate - qint32 rootCount; + int rootCount; in >> rootCount; for (int i = 0; i < rootCount; i++) { AttributePointer attribute; @@ -100,7 +90,7 @@ void MetavoxelData::read(Bitstream& in) { } void MetavoxelData::write(Bitstream& out) const { - out << (qint32)_roots.size(); + out << _roots.size(); for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { out.getAttributeStreamer() << it.key(); it.value()->write(it.key(), out); @@ -108,9 +98,70 @@ void MetavoxelData::write(Bitstream& out) const { } void MetavoxelData::readDelta(const MetavoxelData& reference, Bitstream& in) { + int changedCount; + in >> changedCount; + for (int i = 0; i < changedCount; i++) { + AttributePointer attribute; + in.getAttributeStreamer() >> attribute; + MetavoxelNode*& root = _roots[attribute]; + if (!root) { + root = new MetavoxelNode(attribute); + } + MetavoxelNode* referenceRoot = reference._roots.value(attribute); + if (referenceRoot) { + root->readDelta(attribute, *referenceRoot, in); + + } else { + root->read(attribute, in); + } + } + + int removedCount; + in >> removedCount; + for (int i = 0; i < removedCount; i++) { + AttributePointer attribute; + in.getAttributeStreamer() >> attribute; + + } } void MetavoxelData::writeDelta(const MetavoxelData& reference, Bitstream& out) const { + // count the number of roots added/changed, then write + int changedCount = 0; + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + MetavoxelNode* referenceRoot = reference._roots.value(it.key()); + if (it.value() != referenceRoot) { + changedCount++; + } + } + out << changedCount; + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + MetavoxelNode* referenceRoot = reference._roots.value(it.key()); + if (it.value() != referenceRoot) { + out.getAttributeStreamer() << it.key(); + if (referenceRoot) { + it.value()->writeDelta(it.key(), *referenceRoot, out); + } else { + it.value()->write(it.key(), out); + } + } + } + + // same with nodes removed + int removedCount = 0; + for (QHash::const_iterator it = reference._roots.constBegin(); + it != reference._roots.constEnd(); it++) { + if (!_roots.contains(it.key())) { + removedCount++; + } + } + out << removedCount; + for (QHash::const_iterator it = reference._roots.constBegin(); + it != reference._roots.constEnd(); it++) { + if (!_roots.contains(it.key())) { + out.getAttributeStreamer() << it.key(); + } + } } void MetavoxelData::incrementRootReferenceCounts() { @@ -128,10 +179,11 @@ void MetavoxelData::decrementRootReferenceCounts() { void writeDelta(const MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& out) { if (data == reference) { out << false; - return; + + } else { + out << true; + data->writeDelta(*reference, out); } - out << true; - data->writeDelta(*reference, out); } void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference, Bitstream& in) { @@ -140,6 +192,9 @@ void readDelta(MetavoxelDataPointer& data, const MetavoxelDataPointer& reference if (changed) { data.detach(); data->readDelta(*reference, in); + + } else { + data = reference; } } @@ -150,34 +205,6 @@ MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceC } } -bool MetavoxelNode::setAttributeValue(const MetavoxelPath& path, int index, const AttributeValue& attributeValue) { - if (index == path.getSize()) { - setAttributeValue(attributeValue); - return true; - } - int element = path[index]; - if (_children[element] == NULL) { - AttributeValue ownAttributeValue = getAttributeValue(attributeValue.getAttribute()); - for (int i = 0; i < CHILD_COUNT; i++) { - _children[i] = new MetavoxelNode(ownAttributeValue); - } - } - _children[element]->setAttributeValue(path, index + 1, attributeValue); - - void* childValues[CHILD_COUNT]; - bool allLeaves = true; - for (int i = 0; i < CHILD_COUNT; i++) { - childValues[i] = _children[i]->_attributeValue; - allLeaves &= _children[i]->isLeaf(); - } - if (attributeValue.getAttribute()->merge(_attributeValue, childValues) && allLeaves) { - clearChildren(attributeValue.getAttribute()); - return true; - } - - return false; -} - void MetavoxelNode::setAttributeValue(const AttributeValue& attributeValue) { attributeValue.getAttribute()->destroy(_attributeValue); _attributeValue = attributeValue.copy(); @@ -188,6 +215,18 @@ AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribut return AttributeValue(attribute, _attributeValue); } +void MetavoxelNode::mergeChildren(const AttributePointer& attribute) { + void* childValues[CHILD_COUNT]; + bool allLeaves = true; + for (int i = 0; i < CHILD_COUNT; i++) { + childValues[i] = _children[i]->_attributeValue; + allLeaves &= _children[i]->isLeaf(); + } + if (attribute->merge(_attributeValue, childValues) && allLeaves) { + clearChildren(attribute); + } +} + bool MetavoxelNode::isLeaf() const { for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i]) { @@ -229,11 +268,6 @@ void MetavoxelNode::write(const AttributePointer& attribute, Bitstream& out) con } void MetavoxelNode::readDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& in) { - bool different; - in >> different; - if (!different) { - return; - } bool leaf; in >> leaf; attribute->readDelta(in, _attributeValue, reference._attributeValue, leaf); @@ -254,11 +288,6 @@ void MetavoxelNode::readDelta(const AttributePointer& attribute, const Metavoxel } void MetavoxelNode::writeDelta(const AttributePointer& attribute, const MetavoxelNode& reference, Bitstream& out) const { - if (this == &reference) { - out << false; - return; - } - out << true; bool leaf = isLeaf(); out << leaf; attribute->writeDelta(out, _attributeValue, reference._attributeValue, leaf); @@ -300,18 +329,9 @@ void MetavoxelNode::clearChildren(const AttributePointer& attribute) { } } -int MetavoxelPath::operator[](int index) const { - return (int)_array.at(index * BITS_PER_ELEMENT) | ((int)_array.at(index * BITS_PER_ELEMENT + 1) << 1) | - ((int)_array.at(index * BITS_PER_ELEMENT + 2) << 2); -} - -MetavoxelPath& MetavoxelPath::operator+=(int element) { - int offset = _array.size(); - _array.resize(offset + BITS_PER_ELEMENT); - _array.setBit(offset, element & 0x01); - _array.setBit(offset + 1, (element >> 1) & 0x01); - _array.setBit(offset + 2, element >> 2); - return *this; +MetavoxelVisitor::MetavoxelVisitor(const QVector& inputs, const QVector& outputs) : + _inputs(inputs), + _outputs(outputs) { } PolymorphicData* DefaultMetavoxelGuide::clone() const { @@ -323,42 +343,84 @@ const int Y_MAXIMUM_FLAG = 2; const int Z_MAXIMUM_FLAG = 4; void DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { - visitation.info.isLeaf = visitation.allNodesLeaves(); - if (!visitation.visitor.visit(visitation.info) || visitation.info.isLeaf) { + visitation.info.isLeaf = visitation.allInputNodesLeaves(); + bool keepGoing = visitation.visitor.visit(visitation.info); + for (int i = 0; i < visitation.outputNodes.size(); i++) { + AttributeValue& value = visitation.info.outputValues[i]; + if (value.getAttribute()) { + MetavoxelNode*& node = visitation.outputNodes[i]; + if (!node) { + node = visitation.createOutputNode(i); + } + node->setAttributeValue(value); + } + } + if (!keepGoing) { return; } - MetavoxelVisitation nextVisitation = { visitation.visitor, QVector(visitation.nodes.size()), - { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.nodes.size()) } }; + MetavoxelVisitation nextVisitation = { visitation.data, &visitation, visitation.visitor, + QVector(visitation.inputNodes.size()), QVector(visitation.outputNodes.size()), + { glm::vec3(), visitation.info.size * 0.5f, QVector(visitation.inputNodes.size()), + QVector(visitation.outputNodes.size()) } }; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { - for (int j = 0; j < visitation.nodes.size(); j++) { - MetavoxelNode* node = visitation.nodes.at(j); + for (int j = 0; j < visitation.inputNodes.size(); j++) { + MetavoxelNode* node = visitation.inputNodes.at(j); MetavoxelNode* child = node ? node->getChild(i) : NULL; - nextVisitation.info.attributeValues[j] = ((nextVisitation.nodes[j] = child)) ? - child->getAttributeValue(visitation.info.attributeValues[j].getAttribute()) : - visitation.info.attributeValues[j]; + nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ? + child->getAttributeValue(visitation.info.inputValues[j].getAttribute()) : + visitation.info.inputValues[j]; + } + for (int j = 0; j < visitation.outputNodes.size(); j++) { + MetavoxelNode* node = visitation.outputNodes.at(j); + MetavoxelNode* child = node ? node->getChild(i) : NULL; + nextVisitation.outputNodes[j] = child; } nextVisitation.info.minimum = visitation.info.minimum + glm::vec3( (i & X_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, (i & Y_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f, (i & Z_MAXIMUM_FLAG) ? nextVisitation.info.size : 0.0f); - static_cast(nextVisitation.info.attributeValues.last().getInlineValue< + nextVisitation.childIndex = i; + static_cast(nextVisitation.info.inputValues.last().getInlineValue< PolymorphicDataPointer>().data())->guide(nextVisitation); + for (int j = 0; j < nextVisitation.outputNodes.size(); j++) { + AttributeValue& value = nextVisitation.info.outputValues[j]; + if (value.getAttribute()) { + visitation.info.outputValues[j] = value; + value = AttributeValue(); + } + } + } + for (int i = 0; i < visitation.outputNodes.size(); i++) { + AttributeValue& value = visitation.info.outputValues[i]; + if (value.getAttribute()) { + MetavoxelNode* node = visitation.outputNodes.at(i); + node->mergeChildren(value.getAttribute()); + value = node->getAttributeValue(value.getAttribute()); + } } } -QScriptValue ScriptedMetavoxelGuide::getAttributes(QScriptContext* context, QScriptEngine* engine) { - ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); +static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide, + const QVector& attributes) { - const QVector& attributes = guide->_visitation->visitor.getAttributes(); QScriptValue attributesValue = engine->newArray(attributes.size()); for (int i = 0; i < attributes.size(); i++) { attributesValue.setProperty(i, engine->newQObject(attributes.at(i).data(), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject)); } - return attributesValue; } +QScriptValue ScriptedMetavoxelGuide::getInputs(QScriptContext* context, QScriptEngine* engine) { + ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); + return getAttributes(engine, guide, guide->_visitation->visitor.getInputs()); +} + +QScriptValue ScriptedMetavoxelGuide::getOutputs(QScriptContext* context, QScriptEngine* engine) { + ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); + return getAttributes(engine, guide, guide->_visitation->visitor.getOutputs()); +} + QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) { ScriptedMetavoxelGuide* guide = static_cast(context->callee().data().toVariant().value()); @@ -367,26 +429,26 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin QScriptValue minimum = infoValue.property(guide->_minimumHandle); MetavoxelInfo info = { glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()), - infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.attributeValues, - infoValue.property(guide->_isLeafHandle).toBool() }; + infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.inputValues, + guide->_visitation->info.outputValues, infoValue.property(guide->_isLeafHandle).toBool() }; // extract and convert the values provided by the script - QScriptValue attributeValues = infoValue.property(guide->_attributeValuesHandle); - const QVector& attributes = guide->_visitation->visitor.getAttributes(); - for (int i = 0; i < attributes.size(); i++) { - QScriptValue attributeValue = attributeValues.property(i); + QScriptValue inputValues = infoValue.property(guide->_inputValuesHandle); + const QVector& inputs = guide->_visitation->visitor.getInputs(); + for (int i = 0; i < inputs.size(); i++) { + QScriptValue attributeValue = inputValues.property(i); if (attributeValue.isValid()) { - info.attributeValues[i] = AttributeValue(attributes.at(i), - attributes.at(i)->createFromScript(attributeValue, engine)); + info.inputValues[i] = AttributeValue(inputs.at(i), + inputs.at(i)->createFromScript(attributeValue, engine)); } } QScriptValue result = guide->_visitation->visitor.visit(info); // destroy any created values - for (int i = 0; i < attributes.size(); i++) { - if (attributeValues.property(i).isValid()) { - info.attributeValues[i].getAttribute()->destroy(info.attributeValues[i].getValue()); + for (int i = 0; i < inputs.size(); i++) { + if (inputValues.property(i).isValid()) { + info.inputValues[i].getAttribute()->destroy(info.inputValues[i].getValue()); } } @@ -397,16 +459,19 @@ ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction _guideFunction(guideFunction), _minimumHandle(guideFunction.engine()->toStringHandle("minimum")), _sizeHandle(guideFunction.engine()->toStringHandle("size")), - _attributeValuesHandle(guideFunction.engine()->toStringHandle("attributeValues")), + _inputValuesHandle(guideFunction.engine()->toStringHandle("inputValues")), + _outputValuesHandle(guideFunction.engine()->toStringHandle("outputValues")), _isLeafHandle(guideFunction.engine()->toStringHandle("isLeaf")), - _getAttributesFunction(guideFunction.engine()->newFunction(getAttributes, 0)), + _getInputsFunction(guideFunction.engine()->newFunction(getInputs, 0)), + _getOutputsFunction(guideFunction.engine()->newFunction(getOutputs, 0)), _visitFunction(guideFunction.engine()->newFunction(visit, 1)), _info(guideFunction.engine()->newObject()), _minimum(guideFunction.engine()->newArray(3)) { _arguments.append(guideFunction.engine()->newObject()); QScriptValue visitor = guideFunction.engine()->newObject(); - visitor.setProperty("getAttributes", _getAttributesFunction); + visitor.setProperty("getInputs", _getInputsFunction); + visitor.setProperty("getOutputs", _getOutputsFunction); visitor.setProperty("visit", _visitFunction); _arguments[0].setProperty("visitor", visitor); _arguments[0].setProperty("info", _info); @@ -419,7 +484,7 @@ PolymorphicData* ScriptedMetavoxelGuide::clone() const { void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue(this)); - _getAttributesFunction.setData(data); + _getInputsFunction.setData(data); _visitFunction.setData(data); _minimum.setProperty(0, visitation.info.minimum.x); _minimum.setProperty(1, visitation.info.minimum.y); @@ -433,11 +498,29 @@ void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } } -bool MetavoxelVisitation::allNodesLeaves() const { - foreach (MetavoxelNode* node, nodes) { +bool MetavoxelVisitation::allInputNodesLeaves() const { + foreach (MetavoxelNode* node, inputNodes) { if (node != NULL && !node->isLeaf()) { return false; } } return true; } + +MetavoxelNode* MetavoxelVisitation::createOutputNode(int index) { + const AttributePointer& attribute = visitor.getOutputs().at(index); + if (previous) { + MetavoxelNode*& parent = previous->outputNodes[index]; + if (!parent) { + parent = previous->createOutputNode(index); + } + AttributeValue value = parent->getAttributeValue(attribute); + for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { + parent->_children[i] = new MetavoxelNode(value); + } + return parent->_children[childIndex]; + + } else { + return data->_roots[attribute] = new MetavoxelNode(attribute); + } +} diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index d588c8a687..a25e0bc9a8 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -24,7 +24,6 @@ class QScriptContext; class MetavoxelNode; -class MetavoxelPath; class MetavoxelVisitation; class MetavoxelVisitor; @@ -41,12 +40,6 @@ public: /// Applies the specified visitor to the contained voxels. void guide(MetavoxelVisitor& visitor); - /// Sets the attribute value corresponding to the specified path. - void setAttributeValue(const MetavoxelPath& path, const AttributeValue& attributeValue); - - /// Retrieves the attribute value corresponding to the specified path. - AttributeValue getAttributeValue(const MetavoxelPath& path, const AttributePointer& attribute) const; - void read(Bitstream& in); void write(Bitstream& out) const; @@ -54,6 +47,8 @@ public: void writeDelta(const MetavoxelData& reference, Bitstream& out) const; private: + + friend class MetavoxelVisitation; void incrementRootReferenceCounts(); void decrementRootReferenceCounts(); @@ -74,17 +69,13 @@ public: static const int CHILD_COUNT = 8; MetavoxelNode(const AttributeValue& attributeValue); - - /// Descends the voxel tree in order to set the value of a node. - /// \param path the path to follow - /// \param index the position in the path - /// \return whether or not the node is entirely equal to the value - bool setAttributeValue(const MetavoxelPath& path, int index, const AttributeValue& attributeValue); - + void setAttributeValue(const AttributeValue& attributeValue); AttributeValue getAttributeValue(const AttributePointer& attribute) const; + void mergeChildren(const AttributePointer& attribute); + MetavoxelNode* getChild(int index) const { return _children[index]; } void setChild(int index, MetavoxelNode* child) { _children[index] = child; } @@ -108,6 +99,8 @@ public: private: Q_DISABLE_COPY(MetavoxelNode) + friend class MetavoxelVisitation; + void clearChildren(const AttributePointer& attribute); int _referenceCount; @@ -115,31 +108,14 @@ private: MetavoxelNode* _children[CHILD_COUNT]; }; -/// A path down an octree. -class MetavoxelPath { -public: - - int getSize() const { return _array.size() / BITS_PER_ELEMENT; } - bool isEmpty() const { return _array.isEmpty(); } - - int operator[](int index) const; - - MetavoxelPath& operator+=(int element); - -private: - - static const int BITS_PER_ELEMENT = 3; - - QBitArray _array; -}; - /// Contains information about a metavoxel (explicit or procedural). class MetavoxelInfo { public: glm::vec3 minimum; ///< the minimum extent of the area covered by the voxel float size; ///< the size of the voxel in all dimensions - QVector attributeValues; + QVector inputValues; + QVector outputValues; bool isLeaf; }; @@ -147,19 +123,23 @@ public: class MetavoxelVisitor { public: - MetavoxelVisitor(const QVector& attributes) : _attributes(attributes) { } - - /// Returns a reference to the list of attributes desired. - const QVector& getAttributes() const { return _attributes; } + MetavoxelVisitor(const QVector& inputs, const QVector& outputs); + + /// Returns a reference to the list of input attributes desired. + const QVector& getInputs() const { return _inputs; } + + /// Returns a reference to the list of output attributes provided. + const QVector& getOutputs() const { return _outputs; } /// Visits a metavoxel. - /// \param info the metavoxel ata - /// \param if true, continue descending; if false, stop - virtual bool visit(const MetavoxelInfo& info) = 0; + /// \param info the metavoxel data + /// \return if true, continue descending; if false, stop + virtual bool visit(MetavoxelInfo& info) = 0; protected: - QVector _attributes; + QVector _inputs; + QVector _outputs; }; /// Interface for objects that guide metavoxel visitors. @@ -191,16 +171,19 @@ public: private: - static QScriptValue getAttributes(QScriptContext* context, QScriptEngine* engine); + static QScriptValue getInputs(QScriptContext* context, QScriptEngine* engine); + static QScriptValue getOutputs(QScriptContext* context, QScriptEngine* engine); static QScriptValue visit(QScriptContext* context, QScriptEngine* engine); QScriptValue _guideFunction; QScriptString _minimumHandle; QScriptString _sizeHandle; - QScriptString _attributeValuesHandle; + QScriptString _inputValuesHandle; + QScriptString _outputValuesHandle; QScriptString _isLeafHandle; QScriptValueList _arguments; - QScriptValue _getAttributesFunction; + QScriptValue _getInputsFunction; + QScriptValue _getOutputsFunction; QScriptValue _visitFunction; QScriptValue _info; QScriptValue _minimum; @@ -212,11 +195,16 @@ private: class MetavoxelVisitation { public: + MetavoxelData* data; + MetavoxelVisitation* previous; MetavoxelVisitor& visitor; - QVector nodes; + QVector inputNodes; + QVector outputNodes; MetavoxelInfo info; + int childIndex; - bool allNodesLeaves() const; + bool allInputNodesLeaves() const; + MetavoxelNode* createOutputNode(int index); }; #endif /* defined(__interface__MetavoxelData__) */