Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Philip Rosedale 2013-12-17 20:12:31 -08:00
commit b3bb33e48c
17 changed files with 1445 additions and 8 deletions

View file

@ -93,6 +93,7 @@ include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(metavoxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
@ -135,7 +136,7 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES})
endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR)
qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Svg WebKit WebKitWidgets)
qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets)
# include headers for interface and InterfaceConfig.
include_directories(

View file

@ -0,0 +1,171 @@
//
// sphere.js
// interface
//
// Created by Andrzej Kapolka on 12/17/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
function strictIndexOf(array, element) {
for (var i = 0; i < array.length; i++) {
if (array[i] == element) {
return i;
}
}
return -1;
}
var colorIndex;
var normalIndex;
var visitor;
var info;
var MAX_DEPTH = 4;
var sphereCenter = [ 0.5, 0.5, 0.5 ];
var sphereColor = 0xFFFF00FF;
var sphereRadius = 0.25;
var sphereRadiusSquared = sphereRadius * sphereRadius;
function lengthSquared(x, y, z) {
return x*x + y*y + z*z;
}
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;
} else {
var scale = 127.0 / length;
info.attributeValues[normalIndex] =
(Math.floor(vector[0] * scale) & 0xFF) << 16 |
(Math.floor(vector[1] * scale) & 0xFF) << 8 |
Math.floor(vector[2] * scale) & 0xFF;
}
}
}
function guide(minimum, size, depth) {
info.minimum = minimum;
info.size = size;
// start with a relative fast bounding volume test to find most non-intersecting states
var maximum = [ minimum[0] + size, minimum[1] + size, minimum[2] + size ];
if (minimum[0] >= sphereCenter[0] + sphereRadius ||
minimum[1] >= sphereCenter[1] + sphereRadius ||
minimum[2] >= sphereCenter[2] + sphereRadius ||
maximum[0] <= sphereCenter[0] - sphereRadius ||
maximum[1] <= sphereCenter[1] - sphereRadius ||
maximum[2] <= sphereCenter[2] - sphereRadius) {
info.isLeaf = true;
if (colorIndex != -1) {
info.attributeValues[colorIndex] = 0x0;
}
visitor.visit(info);
return;
}
var halfSize = size / 2;
var center = [ minimum[0] + halfSize, minimum[1] + halfSize, minimum[2] + halfSize ];
var vector = [ center[0] - sphereCenter[0], center[1] - sphereCenter[1], center[2] - sphereCenter[2] ];
// count the number of points inside the sphere
var inside = 0;
if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - minimum[2]) <=
sphereRadiusSquared) {
inside++;
}
if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - minimum[2]) <=
sphereRadiusSquared) {
inside++;
}
if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - minimum[2]) <=
sphereRadiusSquared) {
inside++;
}
if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - minimum[2]) <=
sphereRadiusSquared) {
inside++;
}
if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - maximum[2]) <=
sphereRadiusSquared) {
inside++;
}
if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - minimum[1], sphereCenter[2] - maximum[2]) <=
sphereRadiusSquared) {
inside++;
}
if (lengthSquared(sphereCenter[0] - minimum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - maximum[2]) <=
sphereRadiusSquared) {
inside++;
}
if (lengthSquared(sphereCenter[0] - maximum[0], sphereCenter[1] - maximum[1], sphereCenter[2] - maximum[2]) <=
sphereRadiusSquared) {
inside++;
}
// see if all points are in the sphere
if (inside == 8) {
info.isLeaf = true;
if (colorIndex != -1) {
info.attributeValues[colorIndex] = sphereColor;
}
setNormal(vector);
visitor.visit(info);
return;
}
// if we've reached max depth, compute alpha using a volume estimate
if (depth == MAX_DEPTH) {
info.isLeaf = true;
if (inside >= 3) {
if (colorIndex != -1) {
info.attributeValues[colorIndex] = sphereColor;
}
setNormal(vector);
} else {
if (colorIndex != -1) {
info.attributeValues[colorIndex] = 0x0;
}
}
visitor.visit(info);
return;
}
// recurse
info.isLeaf = false;
if (!visitor.visit(info)) {
return;
}
depth += 1;
guide(minimum, halfSize, depth);
guide([ center[0], minimum[1], minimum[2] ], halfSize, depth);
guide([ minimum[0], center[1], minimum[2] ], halfSize, depth);
guide([ center[0], center[1], minimum[2] ], halfSize, depth);
guide([ minimum[0], minimum[1], center[2] ], halfSize, depth);
guide([ center[0], minimum[1], center[2] ], halfSize, depth);
guide([ minimum[0], center[1], center[2] ], halfSize, depth);
guide([ center[0], center[1], center[2] ], halfSize, depth);
}
(function(visitation) {
var attributes = visitation.visitor.getAttributes();
colorIndex = strictIndexOf(attributes, AttributeRegistry.colorAttribute);
normalIndex = strictIndexOf(attributes, AttributeRegistry.normalAttribute);
visitor = visitation.visitor;
info = { attributeValues: new Array(attributes.length) };
// have the sphere orbit the center and pulse in size
var time = new Date().getTime();
var ROTATE_PERIOD = 400.0;
sphereCenter[0] = 0.5 + 0.25 * Math.cos(time / ROTATE_PERIOD);
sphereCenter[2] = 0.5 + 0.25 * Math.sin(time / ROTATE_PERIOD);
var PULSE_PERIOD = 300.0;
sphereRadius = 0.25 + 0.0625 * Math.cos(time / PULSE_PERIOD);
sphereRadiusSquared = sphereRadius * sphereRadius;
guide(visitation.info.minimum, visitation.info.size, 0);
})

View file

@ -0,0 +1,25 @@
#version 120
//
// metavoxel_point.vert
// vertex shader
//
// Created by Andrzej Kapolka on 12/12/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
uniform float pointScale;
void main(void) {
// standard diffuse lighting
gl_FrontColor = vec4(gl_Color.rgb * (gl_LightModel.ambient.rgb + gl_LightSource[0].ambient.rgb +
gl_LightSource[0].diffuse.rgb * max(0.0, dot(gl_NormalMatrix * gl_Normal, gl_LightSource[0].position.xyz))),
gl_Color.a);
// extract the first three components of the vertex for position
gl_Position = gl_ModelViewProjectionMatrix * vec4(gl_Vertex.xyz, 1.0);
// the final component is the size in world space
gl_PointSize = pointScale * gl_Vertex.w / gl_Position.w;
}

View file

@ -1858,6 +1858,8 @@ void Application::init() {
_particles.init();
_particles.setViewFrustum(getViewFrustum());
_metavoxels.init();
_particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_myAvatar);
_palette.init(_glWidget->width(), _glWidget->height());
@ -2376,6 +2378,15 @@ void Application::updateParticles(float deltaTime) {
}
}
void Application::updateMetavoxels(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()");
if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) {
_metavoxels.simulate(deltaTime);
}
}
void Application::updateTransmitter(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateTransmitter()");
@ -2521,6 +2532,7 @@ void Application::update(float deltaTime) {
updateAvatars(deltaTime, mouseRayOrigin, mouseRayDirection); //loop through all the other avatars and simulate them...
updateMyAvatarSimulation(deltaTime); // Simulate myself
updateParticles(deltaTime); // Simulate particle cloud movements
updateMetavoxels(deltaTime); // update metavoxels
updateTransmitter(deltaTime); // transmitter drive or pick
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
updateDialogs(deltaTime); // update various stats dialogs if present
@ -3068,6 +3080,13 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
}
}
// also, metavoxels
if (Menu::getInstance()->isOptionChecked(MenuOption::Metavoxels)) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... metavoxels...");
_metavoxels.render();
}
// render particles...
_particles.render();

View file

@ -58,9 +58,10 @@
#include "renderer/AmbientOcclusionEffect.h"
#include "renderer/GeometryCache.h"
#include "renderer/GlowEffect.h"
#include "renderer/VoxelShader.h"
#include "renderer/MetavoxelSystem.h"
#include "renderer/PointShader.h"
#include "renderer/TextureCache.h"
#include "renderer/VoxelShader.h"
#include "ui/BandwidthDialog.h"
#include "ui/ChatEntry.h"
#include "ui/VoxelStatsDialog.h"
@ -282,6 +283,7 @@ private:
void updateThreads(float deltaTime);
void updateMyAvatarSimulation(float deltaTime);
void updateParticles(float deltaTime);
void updateMetavoxels(float deltaTime);
void updateTransmitter(float deltaTime);
void updateCamera(float deltaTime);
void updateDialogs(float deltaTime);
@ -362,6 +364,8 @@ private:
QByteArray _voxelsFilename;
bool _wantToKillLocalVoxels;
MetavoxelSystem _metavoxels;
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
Oscilloscope _audioScope;

View file

@ -174,9 +174,8 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples,
if (sourceAudioFormat == destinationAudioFormat) {
memcpy(destinationSamples, sourceSamples, numSourceSamples * sizeof(int16_t));
} else {
int destinationChannels = (destinationAudioFormat.channelCount() >= 2) ? 2 : destinationAudioFormat.channelCount();
float sourceToDestinationFactor = (sourceAudioFormat.sampleRate() / (float) destinationAudioFormat.sampleRate())
* (sourceAudioFormat.channelCount() / (float) destinationChannels);
* (sourceAudioFormat.channelCount() / (float) destinationAudioFormat.channelCount());
// take into account the number of channels in source and destination
// accomodate for the case where have an output with > 2 channels
@ -203,14 +202,15 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples,
// upsample from 24 to 48
// for now this only supports a stereo to stereo conversion - this is our case for network audio to output
int sourceIndex = 0;
int destinationToSourceFactor = (1 / sourceToDestinationFactor);
int dtsSampleRateFactor = (destinationAudioFormat.sampleRate() / sourceAudioFormat.sampleRate());
int sampleShift = destinationAudioFormat.channelCount() * dtsSampleRateFactor;
int destinationToSourceFactor = (1 / sourceToDestinationFactor);
for (int i = 0; i < numDestinationSamples; i += destinationAudioFormat.channelCount() * dtsSampleRateFactor) {
for (int i = 0; i < numDestinationSamples; i += sampleShift) {
sourceIndex = (i / destinationToSourceFactor);
// fill the L/R channels and make the rest silent
for (int j = i; j < i + (dtsSampleRateFactor * destinationAudioFormat.channelCount()); j++) {
for (int j = i; j < i + sampleShift; j++) {
if (j % destinationAudioFormat.channelCount() == 0) {
// left channel
destinationSamples[j] = sourceSamples[sourceIndex];

View file

@ -285,7 +285,8 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ParticleCloud, 0, false);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, false);
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options");

View file

@ -219,6 +219,7 @@ namespace MenuOption {
const QString Login = "Login";
const QString LookAtIndicator = "Look-at Indicator";
const QString LookAtVectors = "Look-at Vectors";
const QString Metavoxels = "Metavoxels";
const QString Mirror = "Mirror";
const QString MoveWithLean = "Move with Lean";
const QString NewVoxelCullingMode = "New Voxel Culling Mode";

View file

@ -0,0 +1,122 @@
//
// MetavoxelSystem.cpp
// interface
//
// Created by Andrzej Kapolka on 12/10/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QFile>
#include <QTextStream>
#include <QtDebug>
#include <SharedUtil.h>
#include "Application.h"
#include "MetavoxelSystem.h"
ProgramObject MetavoxelSystem::_program;
int MetavoxelSystem::_pointScaleLocation;
MetavoxelSystem::MetavoxelSystem() :
_pointVisitor(_points),
_buffer(QOpenGLBuffer::VertexBuffer) {
}
void MetavoxelSystem::init() {
if (!_program.isLinked()) {
switchToResourcesParentIfRequired();
_program.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/metavoxel_point.vert");
_program.link();
_pointScaleLocation = _program.uniformLocation("pointScale");
}
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();
}
void MetavoxelSystem::simulate(float deltaTime) {
_points.clear();
_data.guide(_pointVisitor);
_buffer.bind();
int bytes = _points.size() * sizeof(Point);
if (_buffer.size() < bytes) {
_buffer.allocate(_points.constData(), bytes);
} else {
_buffer.write(0, _points.constData(), bytes);
}
_buffer.release();
}
void MetavoxelSystem::render() {
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
const int VIEWPORT_WIDTH_INDEX = 2;
const int VIEWPORT_HEIGHT_INDEX = 3;
float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX];
float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX];
float viewportDiagonal = sqrtf(viewportWidth*viewportWidth + viewportHeight*viewportHeight);
float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(),
Application::getInstance()->getViewFrustum()->getNearTopRight());
_program.bind();
_program.setUniformValue(_pointScaleLocation, viewportDiagonal *
Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal);
_buffer.bind();
Point* pt = 0;
glVertexPointer(4, GL_FLOAT, sizeof(Point), &pt->vertex);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Point), &pt->color);
glNormalPointer(GL_BYTE, sizeof(Point), &pt->normal);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
glDrawArrays(GL_POINTS, 0, _points.size());
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
_buffer.release();
_program.release();
}
MetavoxelSystem::PointVisitor::PointVisitor(QVector<Point>& points) :
MetavoxelVisitor(QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getColorAttribute() <<
AttributeRegistry::getInstance()->getNormalAttribute()),
_points(points) {
}
bool MetavoxelSystem::PointVisitor::visit(const MetavoxelInfo& info) {
if (!info.isLeaf) {
return true;
}
QRgb color = info.attributeValues.at(0).getInlineValue<QRgb>();
QRgb normal = info.attributeValues.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),
{ qRed(color), qGreen(color), qBlue(color), alpha }, { qRed(normal), qGreen(normal), qBlue(normal) } };
_points.append(point);
}
return false;
}

View file

@ -0,0 +1,61 @@
//
// MetavoxelSystem.h
// interface
//
// Created by Andrzej Kapolka on 12/10/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__MetavoxelSystem__
#define __interface__MetavoxelSystem__
#include <QOpenGLBuffer>
#include <QScriptEngine>
#include <QVector>
#include <glm/glm.hpp>
#include <MetavoxelData.h>
#include "ProgramObject.h"
/// Renders a metavoxel tree.
class MetavoxelSystem {
public:
MetavoxelSystem();
void init();
void simulate(float deltaTime);
void render();
private:
class Point {
public:
glm::vec4 vertex;
quint8 color[4];
quint8 normal[3];
};
class PointVisitor : public MetavoxelVisitor {
public:
PointVisitor(QVector<Point>& points);
virtual bool visit(const MetavoxelInfo& info);
private:
QVector<Point>& _points;
};
static ProgramObject _program;
static int _pointScaleLocation;
QScriptEngine _scriptEngine;
MetavoxelData _data;
QVector<Point> _points;
PointVisitor _pointVisitor;
QOpenGLBuffer _buffer;
};
#endif /* defined(__interface__MetavoxelSystem__) */

View file

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 2.8)
set(ROOT_DIR ../..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
# setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
set(TARGET_NAME metavoxels)
find_package(Qt5Widgets REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME})
qt5_use_modules(${TARGET_NAME} Widgets Script)
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})

View file

@ -0,0 +1,143 @@
//
// AttributeRegistry.cpp
// metavoxels
//
// Created by Andrzej Kapolka on 12/6/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QScriptEngine>
#include "AttributeRegistry.h"
#include "MetavoxelData.h"
AttributeRegistry AttributeRegistry::_instance;
AttributeRegistry::AttributeRegistry() :
_guideAttribute(registerAttribute(new PolymorphicAttribute("guide", PolymorphicDataPointer(new DefaultMetavoxelGuide())))),
_colorAttribute(registerAttribute(new QRgbAttribute("color"))),
_normalAttribute(registerAttribute(new QRgbAttribute("normal", qRgb(0, 127, 0)))) {
}
void AttributeRegistry::configureScriptEngine(QScriptEngine* engine) {
QScriptValue registry = engine->newObject();
registry.setProperty("colorAttribute", engine->newQObject(_colorAttribute.data()));
registry.setProperty("normalAttribute", engine->newQObject(_normalAttribute.data()));
registry.setProperty("getAttribute", engine->newFunction(getAttribute, 1));
engine->globalObject().setProperty("AttributeRegistry", registry);
}
AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute) {
AttributePointer& pointer = _attributes[attribute->getName()];
if (!pointer) {
pointer = attribute;
}
return pointer;
}
QScriptValue AttributeRegistry::getAttribute(QScriptContext* context, QScriptEngine* engine) {
return engine->newQObject(_instance.getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership,
QScriptEngine::PreferExistingWrapperObject);
}
AttributeValue::AttributeValue(const AttributePointer& attribute) :
_attribute(attribute), _value(attribute ? attribute->getDefaultValue() : NULL) {
}
AttributeValue::AttributeValue(const AttributePointer& attribute, void* value) :
_attribute(attribute), _value(value) {
}
void* AttributeValue::copy() const {
return _attribute->create(_value);
}
bool AttributeValue::isDefault() const {
return !_attribute || _attribute->equal(_value, _attribute->getDefaultValue());
}
bool AttributeValue::operator==(const AttributeValue& other) const {
return _attribute == other._attribute && (!_attribute || _attribute->equal(_value, other._value));
}
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, attribute ? attribute->create(value) : NULL) {
}
OwnedAttributeValue::OwnedAttributeValue(const AttributeValue& other) :
AttributeValue(other.getAttribute(), other.getAttribute() ? other.copy() : NULL) {
}
OwnedAttributeValue::~OwnedAttributeValue() {
if (_attribute) {
_attribute->destroy(_value);
}
}
OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) {
if (_attribute) {
_attribute->destroy(_value);
}
if ((_attribute = other.getAttribute())) {
_value = _attribute->create(other.getValue());
}
return *this;
}
Attribute::Attribute(const QString& name) {
setObjectName(name);
}
Attribute::~Attribute() {
}
QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) :
InlineAttribute<QRgb>(name, defaultValue) {
}
bool QRgbAttribute::merge(void*& parent, void* children[]) const {
QRgb firstValue = decodeInline<QRgb>(children[0]);
int totalRed = qRed(firstValue);
int totalGreen = qGreen(firstValue);
int totalBlue = qBlue(firstValue);
int totalAlpha = qAlpha(firstValue);
bool allChildrenEqual = true;
for (int i = 1; i < Attribute::MERGE_COUNT; i++) {
QRgb value = decodeInline<QRgb>(children[i]);
totalRed += qRed(value);
totalGreen += qGreen(value);
totalBlue += qBlue(value);
totalAlpha += qAlpha(value);
allChildrenEqual &= (firstValue == value);
}
parent = encodeInline(qRgba(totalRed / MERGE_COUNT, totalGreen / MERGE_COUNT,
totalBlue / MERGE_COUNT, totalAlpha / MERGE_COUNT));
return allChildrenEqual;
}
void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* engine) const {
return encodeInline((QRgb)value.toUInt32());
}
PolymorphicData::~PolymorphicData() {
}
template<> PolymorphicData* QExplicitlySharedDataPointer<PolymorphicData>::clone() {
return d->clone();
}
PolymorphicAttribute::PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue) :
InlineAttribute<PolymorphicDataPointer>(name, defaultValue) {
}
bool PolymorphicAttribute::merge(void*& parent, void* children[]) const {
return false;
}

View file

@ -0,0 +1,275 @@
//
// AttributeRegistry.h
// metavoxels
//
// Created by Andrzej Kapolka on 12/6/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__AttributeRegistry__
#define __interface__AttributeRegistry__
#include <QColor>
#include <QExplicitlySharedDataPointer>
#include <QHash>
#include <QObject>
#include <QSharedData>
#include <QSharedPointer>
#include <QString>
#include "Bitstream.h"
class QScriptContext;
class QScriptEngine;
class QScriptValue;
class Attribute;
typedef QSharedPointer<Attribute> AttributePointer;
/// Maintains information about metavoxel attribute types.
class AttributeRegistry {
public:
/// Returns a pointer to the singleton registry instance.
static AttributeRegistry* getInstance() { return &_instance; }
AttributeRegistry();
/// Configures the supplied script engine with the global AttributeRegistry property.
void configureScriptEngine(QScriptEngine* engine);
/// Registers an attribute with the system. The registry assumes ownership of the object.
/// \return either the pointer passed as an argument, if the attribute wasn't already registered, or the existing
/// attribute
AttributePointer registerAttribute(Attribute* attribute) { return registerAttribute(AttributePointer(attribute)); }
/// Registers an attribute with the system.
/// \return either the pointer passed as an argument, if the attribute wasn't already registered, or the existing
/// attribute
AttributePointer registerAttribute(AttributePointer attribute);
/// Retrieves an attribute by name.
AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); }
/// Returns a reference to the standard PolymorphicDataPointer "guide" attribute.
const AttributePointer& getGuideAttribute() const { return _guideAttribute; }
/// Returns a reference to the standard QRgb "color" attribute.
const AttributePointer& getColorAttribute() const { return _colorAttribute; }
/// Returns a reference to the standard QRgb "normal" attribute.
const AttributePointer& getNormalAttribute() const { return _normalAttribute; }
private:
static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine);
static AttributeRegistry _instance;
QHash<QString, AttributePointer> _attributes;
AttributePointer _guideAttribute;
AttributePointer _colorAttribute;
AttributePointer _normalAttribute;
};
/// Converts a value to a void pointer.
template<class T> inline void* encodeInline(T value) {
return *(void**)&value;
}
/// Extracts a value from a void pointer.
template<class T> inline T decodeInline(void* value) {
return *(T*)&value;
}
/// Pairs an attribute value with its type.
class AttributeValue {
public:
AttributeValue(const AttributePointer& attribute = AttributePointer());
AttributeValue(const AttributePointer& attribute, void* value);
AttributePointer getAttribute() const { return _attribute; }
void* getValue() const { return _value; }
template<class T> void setInlineValue(T value) { _value = encodeInline(value); }
template<class T> T getInlineValue() const { return decodeInline<T>(_value); }
template<class T> T* getPointerValue() const { return static_cast<T*>(_value); }
void* copy() const;
bool isDefault() const;
bool operator==(const AttributeValue& other) const;
bool operator==(void* other) const;
protected:
AttributePointer _attribute;
void* _value;
};
// Assumes ownership of an attribute value.
class OwnedAttributeValue : public AttributeValue {
public:
OwnedAttributeValue(const AttributePointer& attribute = AttributePointer());
OwnedAttributeValue(const AttributePointer& attribute, void* value);
OwnedAttributeValue(const AttributeValue& other);
~OwnedAttributeValue();
OwnedAttributeValue& operator=(const AttributeValue& other);
};
/// Represents a registered attribute.
class Attribute : public QObject {
Q_OBJECT
public:
static const int MERGE_COUNT = 8;
Attribute(const QString& name);
virtual ~Attribute();
Q_INVOKABLE QString getName() const { return objectName(); }
void* create() const { return create(getDefaultValue()); }
virtual void* create(void* copy) const = 0;
virtual void destroy(void* value) const = 0;
virtual bool read(Bitstream& in, void*& value) const = 0;
virtual bool write(Bitstream& out, void* value) const = 0;
virtual bool equal(void* first, void* second) const = 0;
/// Merges the value of a parent and its children.
/// \return whether or not the children and parent values are all equal
virtual bool merge(void*& parent, void* children[]) const = 0;
virtual void* getDefaultValue() const = 0;
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); }
};
/// A simple attribute class that stores its values inline.
template<class T, int bits = 32> class InlineAttribute : public Attribute {
public:
InlineAttribute(const QString& name, const T& defaultValue = T()) : Attribute(name), _defaultValue(defaultValue) { }
virtual void* create(void* copy) const { void* value; new (&value) T(*(T*)&copy); return value; }
virtual void destroy(void* value) const { ((T*)&value)->~T(); }
virtual bool read(Bitstream& in, void*& value) const { value = getDefaultValue(); in.read(&value, bits); return false; }
virtual bool write(Bitstream& out, void* value) const { out.write(&value, bits); return false; }
virtual bool equal(void* first, void* second) const { return decodeInline<T>(first) == decodeInline<T>(second); }
virtual void* getDefaultValue() const { return encodeInline(_defaultValue); }
private:
T _defaultValue;
};
/// Provides merging using the =, ==, += and /= operators.
template<class T, int bits = 32> class SimpleInlineAttribute : public InlineAttribute<T, bits> {
public:
SimpleInlineAttribute(const QString& name, T defaultValue = T()) : InlineAttribute<T, bits>(name, defaultValue) { }
virtual bool merge(void*& parent, void* children[]) const;
};
template<class T, int bits> inline bool SimpleInlineAttribute<T, bits>::merge(void*& parent, void* children[]) const {
T& merged = *(T*)&parent;
merged = decodeInline<T>(children[0]);
bool allChildrenEqual = true;
for (int i = 1; i < Attribute::MERGE_COUNT; i++) {
merged += decodeInline<T>(children[i]);
allChildrenEqual &= (decodeInline<T>(children[0]) == decodeInline<T>(children[i]));
}
merged /= Attribute::MERGE_COUNT;
return allChildrenEqual;
}
/// Provides appropriate averaging for RGBA values.
class QRgbAttribute : public InlineAttribute<QRgb> {
public:
QRgbAttribute(const QString& name, QRgb defaultValue = QRgb());
virtual bool merge(void*& parent, void* children[]) const;
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const;
};
/// An attribute class that stores pointers to its values.
template<class T> class PointerAttribute : public Attribute {
public:
PointerAttribute(const QString& name, T defaultValue = T()) : Attribute(name), _defaultValue(defaultValue) { }
virtual void* create(void* copy) const { new T(*static_cast<T*>(copy)); }
virtual void destroy(void* value) const { delete static_cast<T*>(value); }
virtual bool read(Bitstream& in, void*& value) const { in >> *static_cast<T*>(value); return true; }
virtual bool write(Bitstream& out, void* value) const { out << *static_cast<T*>(value); return true; }
virtual bool equal(void* first, void* second) const { return *static_cast<T*>(first) == *static_cast<T*>(second); }
virtual void* getDefaultValue() const { return const_cast<void*>((void*)&_defaultValue); }
private:
T _defaultValue;
};
/// Provides merging using the =, ==, += and /= operators.
template<class T> class SimplePointerAttribute : public PointerAttribute<T> {
public:
SimplePointerAttribute(const QString& name, T defaultValue = T()) : PointerAttribute<T>(name, defaultValue) { }
virtual bool merge(void*& parent, void* children[]) const;
};
template<class T> inline bool SimplePointerAttribute<T>::merge(void*& parent, void* children[]) const {
T& merged = *static_cast<T*>(parent);
merged = *static_cast<T*>(children[0]);
bool allChildrenEqual = true;
for (int i = 1; i < Attribute::MERGE_COUNT; i++) {
merged += *static_cast<T*>(children[i]);
allChildrenEqual &= (*static_cast<T*>(children[0]) == *static_cast<T*>(children[i]));
}
merged /= Attribute::MERGE_COUNT;
return allChildrenEqual;
}
/// Base class for polymorphic attribute data.
class PolymorphicData : public QSharedData {
public:
virtual ~PolymorphicData();
/// Creates a new clone of this object.
virtual PolymorphicData* clone() const = 0;
};
template<> PolymorphicData* QExplicitlySharedDataPointer<PolymorphicData>::clone();
typedef QExplicitlySharedDataPointer<PolymorphicData> PolymorphicDataPointer;
/// Provides polymorphic streaming and averaging.
class PolymorphicAttribute : public InlineAttribute<PolymorphicDataPointer> {
public:
PolymorphicAttribute(const QString& name, const PolymorphicDataPointer& defaultValue = PolymorphicDataPointer());
virtual bool merge(void*& parent, void* children[]) const;
};
#endif /* defined(__interface__AttributeRegistry__) */

View file

@ -0,0 +1,81 @@
//
// Bitstream.cpp
// metavoxels
//
// Created by Andrzej Kapolka on 12/2/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QDataStream>
#include "Bitstream.h"
Bitstream::Bitstream(QDataStream& underlying)
: _underlying(underlying), _byte(0), _position(0) {
}
const int BITS_IN_BYTE = 8;
const int LAST_BIT_POSITION = BITS_IN_BYTE - 1;
Bitstream& Bitstream::write(const void* data, int bits, int offset) {
const quint8* source = (const quint8*)data;
while (bits > 0) {
int bitsToWrite = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits));
_byte |= ((*source >> offset) & ((1 << bitsToWrite) - 1)) << _position;
if ((_position += bitsToWrite) == BITS_IN_BYTE) {
flush();
}
if ((offset += bitsToWrite) == BITS_IN_BYTE) {
source++;
offset = 0;
}
bits -= bitsToWrite;
}
return *this;
}
Bitstream& Bitstream::read(void* data, int bits, int offset) {
quint8* dest = (quint8*)data;
while (bits > 0) {
if (_position == 0) {
_underlying >> _byte;
}
int bitsToRead = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits));
*dest |= ((_byte >> _position) & ((1 << bitsToRead) - 1)) << offset;
_position = (_position + bitsToRead) & LAST_BIT_POSITION;
if ((offset += bitsToRead) == BITS_IN_BYTE) {
dest++;
offset = 0;
}
bits -= bitsToRead;
}
return *this;
}
void Bitstream::flush() {
if (_position != 0) {
_underlying << _byte;
_byte = 0;
_position = 0;
}
}
Bitstream& Bitstream::operator<<(bool value) {
if (value) {
_byte |= (1 << _position);
}
if (++_position == BITS_IN_BYTE) {
flush();
}
return *this;
}
Bitstream& Bitstream::operator>>(bool& value) {
if (_position == 0) {
_underlying >> _byte;
}
value = _byte & (1 << _position);
_position = (_position + 1) & LAST_BIT_POSITION;
return *this;
}

View file

@ -0,0 +1,45 @@
//
// Bitstream.h
// metavoxels
//
// Created by Andrzej Kapolka on 12/2/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__Bitstream__
#define __interface__Bitstream__
#include <QtGlobal>
class QDataStream;
/// A stream for bit-aligned data.
class Bitstream {
public:
Bitstream(QDataStream& underlying);
/// Writes a set of bits to the underlying stream.
/// \param bits the number of bits to write
/// \param offset the offset of the first bit
Bitstream& write(const void* data, int bits, int offset = 0);
/// Reads a set of bits from the underlying stream.
/// \param bits the number of bits to read
/// \param offset the offset of the first bit
Bitstream& read(void* data, int bits, int offset = 0);
/// Flushes any unwritten bits to the underlying stream.
void flush();
Bitstream& operator<<(bool value);
Bitstream& operator>>(bool& value);
private:
QDataStream& _underlying;
quint8 _byte;
int _position;
};
#endif /* defined(__interface__Bitstream__) */

View file

@ -0,0 +1,281 @@
//
// MetavoxelData.cpp
// metavoxels
//
// Created by Andrzej Kapolka on 12/6/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <QScriptEngine>
#include <QtDebug>
#include "MetavoxelData.h"
MetavoxelData::~MetavoxelData() {
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) {
it.value()->destroy(it.key());
delete it.value();
}
}
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];
}
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<
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->destroy(attributeValue.getAttribute());
delete node;
_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);
}
node = child;
}
return node->getAttributeValue(attribute);
}
MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) {
_attributeValue = attributeValue.copy();
for (int i = 0; i < CHILD_COUNT; i++) {
_children[i] = NULL;
}
}
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();
clearChildren(attributeValue.getAttribute());
}
AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribute) const {
return AttributeValue(attribute, _attributeValue);
}
bool MetavoxelNode::isLeaf() const {
for (int i = 0; i < CHILD_COUNT; i++) {
if (_children[i]) {
return false;
}
}
return true;
}
void MetavoxelNode::destroy(const AttributePointer& attribute) {
attribute->destroy(_attributeValue);
for (int i = 0; i < CHILD_COUNT; i++) {
if (_children[i]) {
_children[i]->destroy(attribute);
delete _children[i];
}
}
}
void MetavoxelNode::clearChildren(const AttributePointer& attribute) {
for (int i = 0; i < CHILD_COUNT; i++) {
if (_children[i]) {
_children[i]->destroy(attribute);
delete _children[i];
_children[i] = NULL;
}
}
}
int MetavoxelPath::operator[](int index) const {
return _array.at(index * BITS_PER_ELEMENT) | (_array.at(index * BITS_PER_ELEMENT + 1) << 1) |
(_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;
}
PolymorphicData* DefaultMetavoxelGuide::clone() const {
return new DefaultMetavoxelGuide();
}
const int X_MAXIMUM_FLAG = 1;
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) {
return;
}
MetavoxelVisitation nextVisitation = { visitation.visitor, QVector<MetavoxelNode*>(visitation.nodes.size()),
{ glm::vec3(), visitation.info.size * 0.5f, QVector<AttributeValue>(visitation.nodes.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);
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.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<
PolymorphicDataPointer>().data())->guide(nextVisitation);
}
}
QScriptValue ScriptedMetavoxelGuide::getAttributes(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
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::visit(QScriptContext* context, QScriptEngine* engine) {
ScriptedMetavoxelGuide* guide = static_cast<ScriptedMetavoxelGuide*>(context->callee().data().toVariant().value<void*>());
// start with the basics, including inherited attribute values
QScriptValue infoValue = context->argument(0);
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() };
// 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);
if (attributeValue.isValid()) {
info.attributeValues[i] = AttributeValue(attributes.at(i),
attributes.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());
}
}
return result;
}
ScriptedMetavoxelGuide::ScriptedMetavoxelGuide(const QScriptValue& guideFunction) :
_guideFunction(guideFunction),
_minimumHandle(guideFunction.engine()->toStringHandle("minimum")),
_sizeHandle(guideFunction.engine()->toStringHandle("size")),
_attributeValuesHandle(guideFunction.engine()->toStringHandle("attributeValues")),
_isLeafHandle(guideFunction.engine()->toStringHandle("isLeaf")),
_getAttributesFunction(guideFunction.engine()->newFunction(getAttributes, 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("visit", _visitFunction);
_arguments[0].setProperty("visitor", visitor);
_arguments[0].setProperty("info", _info);
_info.setProperty(_minimumHandle, _minimum);
}
PolymorphicData* ScriptedMetavoxelGuide::clone() const {
return new ScriptedMetavoxelGuide(_guideFunction);
}
void ScriptedMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
QScriptValue data = _guideFunction.engine()->newVariant(QVariant::fromValue<void*>(this));
_getAttributesFunction.setData(data);
_visitFunction.setData(data);
_minimum.setProperty(0, visitation.info.minimum.x);
_minimum.setProperty(1, visitation.info.minimum.y);
_minimum.setProperty(2, visitation.info.minimum.z);
_info.setProperty(_sizeHandle, visitation.info.size);
_info.setProperty(_isLeafHandle, visitation.info.isLeaf);
_visitation = &visitation;
_guideFunction.call(QScriptValue(), _arguments);
if (_guideFunction.engine()->hasUncaughtException()) {
qDebug() << "Script error: " << _guideFunction.engine()->uncaughtException().toString() << "\n";
}
}
bool MetavoxelVisitation::allNodesLeaves() const {
foreach (MetavoxelNode* node, nodes) {
if (node != NULL && !node->isLeaf()) {
return false;
}
}
return true;
}

View file

@ -0,0 +1,187 @@
//
// MetavoxelData.h
// metavoxels
//
// Created by Andrzej Kapolka on 12/6/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__MetavoxelData__
#define __interface__MetavoxelData__
#include <QBitArray>
#include <QHash>
#include <QScriptString>
#include <QScriptValue>
#include <QVector>
#include <glm/glm.hpp>
#include "AttributeRegistry.h"
class QScriptContext;
class MetavoxelNode;
class MetavoxelPath;
class MetavoxelVisitation;
class MetavoxelVisitor;
/// The base metavoxel representation shared between server and client.
class MetavoxelData {
public:
~MetavoxelData();
/// 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;
private:
QHash<AttributePointer, MetavoxelNode*> _roots;
};
/// A single node within a metavoxel layer.
class MetavoxelNode {
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;
MetavoxelNode* getChild(int index) const { return _children[index]; }
void setChild(int index, MetavoxelNode* child) { _children[index] = child; }
bool isLeaf() const;
void destroy(const AttributePointer& attribute);
private:
Q_DISABLE_COPY(MetavoxelNode)
void clearChildren(const AttributePointer& attribute);
void* _attributeValue;
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;
bool isLeaf;
};
/// Interface for visitors to metavoxels.
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; }
/// Visits a metavoxel.
/// \param info the metavoxel ata
/// \param if true, continue descending; if false, stop
virtual bool visit(const MetavoxelInfo& info) = 0;
protected:
QVector<AttributePointer> _attributes;
};
/// Interface for objects that guide metavoxel visitors.
class MetavoxelGuide : public PolymorphicData {
public:
/// Guides the specified visitor to the contained voxels.
virtual void guide(MetavoxelVisitation& visitation) = 0;
};
/// Guides visitors through the explicit content of the system.
class DefaultMetavoxelGuide : public MetavoxelGuide {
public:
virtual PolymorphicData* clone() const;
virtual void guide(MetavoxelVisitation& visitation);
};
/// Represents a guide implemented in Javascript.
class ScriptedMetavoxelGuide : public MetavoxelGuide {
public:
ScriptedMetavoxelGuide(const QScriptValue& guideFunction);
virtual PolymorphicData* clone() const;
virtual void guide(MetavoxelVisitation& visitation);
private:
static QScriptValue getAttributes(QScriptContext* context, QScriptEngine* engine);
static QScriptValue visit(QScriptContext* context, QScriptEngine* engine);
QScriptValue _guideFunction;
QScriptString _minimumHandle;
QScriptString _sizeHandle;
QScriptString _attributeValuesHandle;
QScriptString _isLeafHandle;
QScriptValueList _arguments;
QScriptValue _getAttributesFunction;
QScriptValue _visitFunction;
QScriptValue _info;
QScriptValue _minimum;
MetavoxelVisitation* _visitation;
};
/// Contains the state associated with a visit to a metavoxel system.
class MetavoxelVisitation {
public:
MetavoxelVisitor& visitor;
QVector<MetavoxelNode*> nodes;
MetavoxelInfo info;
bool allNodesLeaves() const;
};
#endif /* defined(__interface__MetavoxelData__) */