Merge pull request #1627 from ey6es/metavoxels

Basic metavoxel editing interface (doesn't actually perform any edits yet).
This commit is contained in:
AndrewMeadows 2014-01-23 16:37:22 -08:00
commit 6120416cbb
16 changed files with 955 additions and 250 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,6 +12,7 @@
#include <QMenuBar>
#include <QHash>
#include <QKeySequence>
#include <QPointer>
#include <AbstractMenuInterface.h>
@ -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> _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";

View file

@ -6,8 +6,6 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QFile>
#include <QTextStream>
#include <QtDebug>
#include <SharedUtil.h>
@ -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<Point>& points) :
MetavoxelVisitor(QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getColorAttribute() <<
AttributeRegistry::getInstance()->getNormalAttribute()),
AttributeRegistry::getInstance()->getNormalAttribute(),
QVector<AttributePointer>()),
_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>();
QRgb normal = info.attributeValues.at(1).getInlineValue<QRgb>();
QRgb color = info.inputValues.at(0).getInlineValue<QRgb>();
QRgb normal = info.inputValues.at(1).getInlineValue<QRgb>();
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),

View file

@ -11,7 +11,6 @@
#include <QList>
#include <QOpenGLBuffer>
#include <QScriptEngine>
#include <QVector>
#include <glm/glm.hpp>
@ -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<Point>& points);
virtual bool visit(const MetavoxelInfo& info);
virtual bool visit(MetavoxelInfo& info);
private:
QVector<Point>& _points;
@ -67,7 +71,6 @@ private:
static ProgramObject _program;
static int _pointScaleLocation;
QScriptEngine _scriptEngine;
MetavoxelData _data;
QVector<Point> _points;
PointVisitor _pointVisitor;

View file

@ -7,7 +7,11 @@
#include <cmath>
// include this before QOpenGLBuffer, which includes an earlier version of OpenGL
#include "InterfaceConfig.h"
#include <QNetworkReply>
#include <QOpenGLBuffer>
#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<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url) {
QSharedPointer<NetworkGeometry> geometry = _networkGeometry.value(url);
if (geometry.isNull()) {

View file

@ -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<NetworkGeometry> getGeometry(const QUrl& url);
@ -45,6 +47,7 @@ private:
QHash<IntPair, VerticesIndices> _hemisphereVBOs;
QHash<IntPair, VerticesIndices> _squareVBOs;
QHash<IntPair, VerticesIndices> _halfCylinderVBOs;
QHash<IntPair, QOpenGLBuffer> _gridBuffers;
QHash<QUrl, QWeakPointer<NetworkGeometry> > _networkGeometry;
};

View file

@ -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 <QComboBox>
#include <QDialogButtonBox>
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QLineEdit>
#include <QListWidget>
#include <QMetaProperty>
#include <QPushButton>
#include <QVBoxLayout>
#include <AttributeRegistry.h>
#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<QColor>();
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<QString> 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<QListWidgetItem*> 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<AttributePointer>(), QVector<AttributePointer>() << 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;

View file

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

View file

@ -6,7 +6,10 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QColorDialog>
#include <QPushButton>
#include <QScriptEngine>
#include <QVBoxLayout>
#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<QColor>().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() {
}

View file

@ -16,9 +16,11 @@
#include <QSharedData>
#include <QSharedPointer>
#include <QString>
#include <QWidget>
#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<QString, AttributePointer>& 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.

View file

@ -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<AttributePointer>& attributes = visitor.getAttributes();
MetavoxelVisitation firstVisitation = { visitor, QVector<MetavoxelNode*>(attributes.size() + 1),
{ glm::vec3(), TOP_LEVEL_SIZE, QVector<AttributeValue>(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<AttributePointer>& inputs = visitor.getInputs();
const QVector<AttributePointer>& outputs = visitor.getOutputs();
MetavoxelVisitation firstVisitation = { this, NULL, visitor, QVector<MetavoxelNode*>(inputs.size() + 1),
QVector<MetavoxelNode*>(outputs.size()), { glm::vec3(), TOP_LEVEL_SIZE,
QVector<AttributeValue>(inputs.size() + 1), QVector<AttributeValue>(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<MetavoxelGuide*>(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<MetavoxelGuide*>(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<AttributePointer, MetavoxelNode*>::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<AttributePointer, MetavoxelNode*>::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<AttributePointer, MetavoxelNode*>::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<AttributePointer, MetavoxelNode*>::const_iterator it = reference._roots.constBegin();
it != reference._roots.constEnd(); it++) {
if (!_roots.contains(it.key())) {
removedCount++;
}
}
out << removedCount;
for (QHash<AttributePointer, MetavoxelNode*>::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<AttributePointer>& inputs, const QVector<AttributePointer>& 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<MetavoxelNode*>(visitation.nodes.size()),
{ glm::vec3(), visitation.info.size * 0.5f, QVector<AttributeValue>(visitation.nodes.size()) } };
MetavoxelVisitation nextVisitation = { visitation.data, &visitation, visitation.visitor,
QVector<MetavoxelNode*>(visitation.inputNodes.size()), QVector<MetavoxelNode*>(visitation.outputNodes.size()),
{ glm::vec3(), visitation.info.size * 0.5f, QVector<AttributeValue>(visitation.inputNodes.size()),
QVector<AttributeValue>(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<MetavoxelGuide*>(nextVisitation.info.attributeValues.last().getInlineValue<
nextVisitation.childIndex = i;
static_cast<MetavoxelGuide*>(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<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
static QScriptValue getAttributes(QScriptEngine* engine, ScriptedMetavoxelGuide* guide,
const QVector<AttributePointer>& attributes) {
const QVector<AttributePointer>& 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<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
return getAttributes(engine, guide, guide->_visitation->visitor.getInputs());
}
QScriptValue ScriptedMetavoxelGuide::getOutputs(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
return getAttributes(engine, guide, guide->_visitation->visitor.getOutputs());
}
QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
@ -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<AttributePointer>& 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<AttributePointer>& 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<void*>(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);
}
}

View file

@ -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<AttributeValue> attributeValues;
QVector<AttributeValue> inputValues;
QVector<AttributeValue> outputValues;
bool isLeaf;
};
@ -147,19 +123,23 @@ public:
class MetavoxelVisitor {
public:
MetavoxelVisitor(const QVector<AttributePointer>& attributes) : _attributes(attributes) { }
/// Returns a reference to the list of attributes desired.
const QVector<AttributePointer>& getAttributes() const { return _attributes; }
MetavoxelVisitor(const QVector<AttributePointer>& inputs, const QVector<AttributePointer>& outputs);
/// Returns a reference to the list of input attributes desired.
const QVector<AttributePointer>& getInputs() const { return _inputs; }
/// Returns a reference to the list of output attributes provided.
const QVector<AttributePointer>& 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<AttributePointer> _attributes;
QVector<AttributePointer> _inputs;
QVector<AttributePointer> _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<MetavoxelNode*> nodes;
QVector<MetavoxelNode*> inputNodes;
QVector<MetavoxelNode*> outputNodes;
MetavoxelInfo info;
int childIndex;
bool allNodesLeaves() const;
bool allInputNodesLeaves() const;
MetavoxelNode* createOutputNode(int index);
};
#endif /* defined(__interface__MetavoxelData__) */