Conflicts:
	interface/interface_en.ts
This commit is contained in:
Philip Rosedale 2014-04-01 21:14:51 -07:00
commit ca863e77f4
26 changed files with 2467 additions and 379 deletions

View file

@ -4,22 +4,22 @@
<context>
<name>Application</name>
<message>
<location filename="src/Application.cpp" line="1382"/>
<location filename="src/Application.cpp" line="1385"/>
<source>Export Voxels</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="src/Application.cpp" line="1383"/>
<location filename="src/Application.cpp" line="1386"/>
<source>Sparse Voxel Octree Files (*.svo)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="src/Application.cpp" line="3720"/>
<location filename="src/Application.cpp" line="3714"/>
<source>Open Script</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="src/Application.cpp" line="3721"/>
<location filename="src/Application.cpp" line="3715"/>
<source>JavaScript Files (*.js)</source>
<translation type="unfinished"></translation>
</message>

View file

@ -372,6 +372,9 @@ Application::~Application() {
_nodeThread->quit();
_nodeThread->wait();
// stop the audio process
QMetaObject::invokeMethod(&_audio, "stop");
// ask the audio thread to quit and wait until it is done
_audio.thread()->quit();
_audio.thread()->wait();

View file

@ -96,6 +96,7 @@ void Audio::reset() {
QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) {
QAudioDeviceInfo result;
foreach(QAudioDeviceInfo audioDevice, QAudioDeviceInfo::availableDevices(mode)) {
qDebug() << audioDevice.deviceName() << " " << deviceName;
if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) {
result = audioDevice;
}
@ -163,6 +164,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
qDebug() << "output device:" << woc.szPname;
deviceName = woc.szPname;
}
qDebug() << "DEBUG [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
return getNamedAudioDeviceForMode(mode, deviceName);
#endif
@ -280,8 +283,6 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples,
}
}
const int CALLBACK_ACCELERATOR_RATIO = 2;
void Audio::start() {
// set up the desired audio format
@ -303,9 +304,18 @@ void Audio::start() {
qDebug() << "The default audio output device is" << outputDeviceInfo.deviceName();
bool outputFormatSupported = switchOutputToAudioDevice(outputDeviceInfo);
if (!inputFormatSupported || !outputFormatSupported) {
qDebug() << "Unable to set up audio I/O because of a problem with input or output formats.";
if (!inputFormatSupported) {
qDebug() << "Unable to set up audio input because of a problem with input format.";
}
if (!outputFormatSupported) {
qDebug() << "Unable to set up audio output because of a problem with output format.";
}
}
void Audio::stop() {
// "switch" to invalid devices in order to shut down the state
switchInputToAudioDevice(QAudioDeviceInfo());
switchOutputToAudioDevice(QAudioDeviceInfo());
}
QString Audio::getDefaultDeviceName(QAudio::Mode mode) {
@ -322,10 +332,12 @@ QVector<QString> Audio::getDeviceNames(QAudio::Mode mode) {
}
bool Audio::switchInputToAudioDevice(const QString& inputDeviceName) {
qDebug() << "DEBUG [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]";
return switchInputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName));
}
bool Audio::switchOutputToAudioDevice(const QString& outputDeviceName) {
qDebug() << "DEBUG [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]";
return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName));
}
@ -337,14 +349,13 @@ void Audio::handleAudioInput() {
static int16_t* monoAudioSamples = (int16_t*) (monoAudioDataPacket + leadingBytes);
static float inputToNetworkInputRatio = _numInputCallbackBytes * CALLBACK_ACCELERATOR_RATIO
/ NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL;
float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(_numInputCallbackBytes);
static unsigned int inputSamplesRequired = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio;
unsigned int inputSamplesRequired = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio;
QByteArray inputByteArray = _inputDevice->readAll();
if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted) {
if (Menu::getInstance()->isOptionChecked(MenuOption::EchoLocalAudio) && !_muted && _audioOutput) {
// if this person wants local loopback add that to the locally injected audio
if (!_loopbackOutputDevice && _loopbackAudioOutput) {
@ -357,7 +368,7 @@ void Audio::handleAudioInput() {
_loopbackOutputDevice->write(inputByteArray);
}
} else {
static float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate())
float loopbackOutputToInputRatio = (_outputFormat.sampleRate() / (float) _inputFormat.sampleRate())
* (_outputFormat.channelCount() / _inputFormat.channelCount());
QByteArray loopBackByteArray(inputByteArray.size() * loopbackOutputToInputRatio, 0);
@ -382,9 +393,6 @@ void Audio::handleAudioInput() {
// zero out the monoAudioSamples array and the locally injected audio
memset(monoAudioSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
// zero out the locally injected audio in preparation for audio procedural sounds
memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
if (!_muted) {
// we aren't muted, downsample the input audio
linearResampling((int16_t*) inputAudioSamples,
@ -513,28 +521,8 @@ void Audio::handleAudioInput() {
_lastInputLoudness = 0;
}
// add procedural effects to the appropriate input samples
addProceduralSounds(monoAudioSamples,
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
if (!_proceduralOutputDevice && _proceduralAudioOutput) {
_proceduralOutputDevice = _proceduralAudioOutput->start();
}
// send whatever procedural sounds we want to locally loop back to the _proceduralOutputDevice
QByteArray proceduralOutput;
proceduralOutput.resize(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * _outputFormat.sampleRate() *
_outputFormat.channelCount() * sizeof(int16_t) / (_desiredInputFormat.sampleRate() *
_desiredInputFormat.channelCount()));
linearResampling(_localProceduralSamples,
reinterpret_cast<int16_t*>(proceduralOutput.data()),
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
proceduralOutput.size() / sizeof(int16_t),
_desiredInputFormat, _outputFormat);
if (_proceduralOutputDevice) {
_proceduralOutputDevice->write(proceduralOutput);
if (_proceduralAudioOutput) {
processProceduralAudio(monoAudioSamples, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
}
NodeList* nodeList = NodeList::getInstance();
@ -614,9 +602,37 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
}
}
if (_audioOutput) {
// Audio output must exist and be correctly set up if we're going to process received audio
processReceivedAudio(audioByteArray);
}
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO).updateValue(audioByteArray.size());
_lastReceiveTime = currentReceiveTime;
}
bool Audio::mousePressEvent(int x, int y) {
if (_iconBounds.contains(x, y)) {
toggleMute();
return true;
}
return false;
}
void Audio::toggleMute() {
_muted = !_muted;
muteToggled();
}
void Audio::toggleAudioNoiseReduction() {
_noiseGateEnabled = !_noiseGateEnabled;
}
void Audio::processReceivedAudio(const QByteArray& audioByteArray) {
_ringBuffer.parseData(audioByteArray);
static float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate())
float networkOutputToOutputRatio = (_desiredOutputFormat.sampleRate() / (float) _outputFormat.sampleRate())
* (_desiredOutputFormat.channelCount() / (float) _outputFormat.channelCount());
if (!_ringBuffer.isStarved() && _audioOutput && _audioOutput->bytesFree() == _audioOutput->bufferSize()) {
@ -671,29 +687,36 @@ void Audio::addReceivedAudioToBuffer(const QByteArray& audioByteArray) {
}
delete[] ringBufferSamples;
}
}
Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AUDIO).updateValue(audioByteArray.size());
_lastReceiveTime = currentReceiveTime;
}
bool Audio::mousePressEvent(int x, int y) {
if (_iconBounds.contains(x, y)) {
toggleMute();
return true;
void Audio::processProceduralAudio(int16_t* monoInput, int numSamples) {
// zero out the locally injected audio in preparation for audio procedural sounds
// This is correlated to numSamples, so it really needs to be numSamples * sizeof(sample)
memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL);
// add procedural effects to the appropriate input samples
addProceduralSounds(monoInput, NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
if (!_proceduralOutputDevice) {
_proceduralOutputDevice = _proceduralAudioOutput->start();
}
// send whatever procedural sounds we want to locally loop back to the _proceduralOutputDevice
QByteArray proceduralOutput;
proceduralOutput.resize(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * _outputFormat.sampleRate() *
_outputFormat.channelCount() * sizeof(int16_t) / (_desiredInputFormat.sampleRate() *
_desiredInputFormat.channelCount()));
linearResampling(_localProceduralSamples,
reinterpret_cast<int16_t*>(proceduralOutput.data()),
NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL,
proceduralOutput.size() / sizeof(int16_t),
_desiredInputFormat, _outputFormat);
if (_proceduralOutputDevice) {
_proceduralOutputDevice->write(proceduralOutput);
}
return false;
}
void Audio::toggleMute() {
_muted = !_muted;
muteToggled();
}
void Audio::toggleAudioNoiseReduction() {
_noiseGateEnabled = !_noiseGateEnabled;
}
void Audio::toggleToneInjection() {
@ -845,7 +868,7 @@ bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
// cleanup any previously initialized device
if (_audioInput) {
_audioInput->stop();
disconnect(_inputDevice, 0, 0, 0);
disconnect(_inputDevice);
_inputDevice = NULL;
delete _audioInput;
@ -863,13 +886,12 @@ bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
qDebug() << "The format to be used for audio input is" << _inputFormat;
_audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this);
_numInputCallbackBytes = NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL * _inputFormat.channelCount()
* (_inputFormat.sampleRate() / SAMPLE_RATE)
/ CALLBACK_ACCELERATOR_RATIO;
_numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat);
_audioInput->setBufferSize(_numInputCallbackBytes);
// how do we want to handle input working, but output not working?
_inputRingBuffer.resizeForFrameSize(_numInputCallbackBytes * CALLBACK_ACCELERATOR_RATIO / sizeof(int16_t));
int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes);
_inputRingBuffer.resizeForFrameSize(numFrameSamples);
_inputDevice = _audioInput->start();
connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput()));
@ -926,3 +948,41 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo)
}
return supportedFormat;
}
// The following constant is operating system dependent due to differences in
// the way input audio is handled. The audio input buffer size is inversely
// proportional to the accelerator ratio.
#ifdef Q_OS_WIN
const float Audio::CALLBACK_ACCELERATOR_RATIO = 0.4f;
#endif
#ifdef Q_OS_MAC
const float Audio::CALLBACK_ACCELERATOR_RATIO = 2.0f;
#endif
#ifdef Q_OS_LINUX
const float Audio::CALLBACK_ACCELERATOR_RATIO = 2.0f;
#endif
int Audio::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) {
int numInputCallbackBytes = (int)(((NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL
* format.channelCount()
* (format.sampleRate() / SAMPLE_RATE))
/ CALLBACK_ACCELERATOR_RATIO) + 0.5f);
return numInputCallbackBytes;
}
float Audio::calculateDeviceToNetworkInputRatio(int numBytes) {
float inputToNetworkInputRatio = (int)((_numInputCallbackBytes
* CALLBACK_ACCELERATOR_RATIO
/ NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL) + 0.5f);
return inputToNetworkInputRatio;
}
int Audio::calculateNumberOfFrameSamples(int numBytes) {
int frameSamples = (int)(numBytes * CALLBACK_ACCELERATOR_RATIO + 0.5f) / sizeof(int16_t);
return frameSamples;
}

View file

@ -76,6 +76,7 @@ public:
public slots:
void start();
void stop();
void addReceivedAudioToBuffer(const QByteArray& audioByteArray);
void handleAudioInput();
void reset();
@ -165,11 +166,26 @@ private:
// Audio callback in class context.
inline void performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* outputRight);
// Process procedural audio by
// 1. Echo to the local procedural output device
// 2. Mix with the audio input
void processProceduralAudio(int16_t* monoInput, int numSamples);
// Add sounds that we want the user to not hear themselves, by adding on top of mic input signal
void addProceduralSounds(int16_t* monoInput, int numSamples);
// Process received audio
void processReceivedAudio(const QByteArray& audioByteArray);
bool switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo);
bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo);
// Callback acceleration dependent calculations
static const float CALLBACK_ACCELERATOR_RATIO;
int calculateNumberOfInputCallbackBytes(const QAudioFormat& format);
int calculateNumberOfFrameSamples(int numBytes);
float calculateDeviceToNetworkInputRatio(int numBytes);
};

View file

@ -167,12 +167,14 @@ void MetavoxelSystem::maybeAttachClient(const SharedNodePointer& node) {
MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector<Point>& points) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getColorAttribute() <<
AttributeRegistry::getInstance()->getNormalAttribute()),
QVector<AttributePointer>(), QVector<AttributePointer>() << AttributeRegistry::getInstance()->getColorAttribute() <<
AttributeRegistry::getInstance()->getNormalAttribute() <<
AttributeRegistry::getInstance()->getSpannerColorAttribute() <<
AttributeRegistry::getInstance()->getSpannerNormalAttribute()),
_points(points) {
}
bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner) {
bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
spanner->getRenderer()->simulate(_deltaTime);
return true;
}
@ -186,21 +188,47 @@ int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) {
QRgb color = info.inputValues.at(0).getInlineValue<QRgb>();
QRgb normal = info.inputValues.at(1).getInlineValue<QRgb>();
quint8 alpha = qAlpha(color);
if (alpha > 0) {
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha },
{ quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } };
_points.append(point);
if (!info.isLODLeaf) {
if (alpha > 0) {
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha },
{ quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } };
_points.append(point);
}
} else {
QRgb spannerColor = info.inputValues.at(2).getInlineValue<QRgb>();
QRgb spannerNormal = info.inputValues.at(3).getInlineValue<QRgb>();
quint8 spannerAlpha = qAlpha(spannerColor);
if (spannerAlpha > 0) {
if (alpha > 0) {
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(spannerColor)), quint8(qGreen(spannerColor)), quint8(qBlue(spannerColor)), spannerAlpha },
{ quint8(qRed(spannerNormal)), quint8(qGreen(spannerNormal)), quint8(qBlue(spannerNormal)) } };
_points.append(point);
} else {
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(spannerColor)), quint8(qGreen(spannerColor)), quint8(qBlue(spannerColor)), spannerAlpha },
{ quint8(qRed(spannerNormal)), quint8(qGreen(spannerNormal)), quint8(qBlue(spannerNormal)) } };
_points.append(point);
}
} else if (alpha > 0) {
Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size),
{ quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha },
{ quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } };
_points.append(point);
}
}
return STOP_RECURSION;
}
MetavoxelSystem::RenderVisitor::RenderVisitor() :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute()) {
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannerMaskAttribute()) {
}
bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner) {
spanner->getRenderer()->render(1.0f);
bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
spanner->getRenderer()->render(1.0f, clipMinimum, clipSize);
return true;
}
@ -306,10 +334,55 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) {
}
}
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
GLdouble coefficients[] = { x, y, z, w };
glClipPlane(plane, coefficients);
glEnable(plane);
}
void ClippedRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) {
if (clipSize == 0.0f) {
renderUnclipped(alpha);
return;
}
enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipSize);
enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x);
enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipSize);
enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y);
enableClipPlane(GL_CLIP_PLANE4, 0.0f, 0.0f, -1.0f, clipMinimum.z + clipSize);
enableClipPlane(GL_CLIP_PLANE5, 0.0f, 0.0f, 1.0f, -clipMinimum.z);
renderUnclipped(alpha);
glDisable(GL_CLIP_PLANE0);
glDisable(GL_CLIP_PLANE1);
glDisable(GL_CLIP_PLANE2);
glDisable(GL_CLIP_PLANE3);
glDisable(GL_CLIP_PLANE4);
glDisable(GL_CLIP_PLANE5);
}
SphereRenderer::SphereRenderer() {
}
void SphereRenderer::render(float alpha) {
void SphereRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) {
if (clipSize == 0.0f) {
renderUnclipped(alpha);
return;
}
// slight performance optimization: don't render if clip bounds are entirely within sphere
Sphere* sphere = static_cast<Sphere*>(parent());
Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize));
for (int i = 0; i < Box::VERTEX_COUNT; i++) {
const float CLIP_PROPORTION = 0.95f;
if (glm::distance(sphere->getTranslation(), clipBox.getVertex(i)) >= sphere->getScale() * CLIP_PROPORTION) {
ClippedRenderer::render(alpha, clipMinimum, clipSize);
return;
}
}
}
void SphereRenderer::renderUnclipped(float alpha) {
Sphere* sphere = static_cast<Sphere*>(parent());
const QColor& color = sphere->getColor();
glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha);
@ -357,11 +430,12 @@ void StaticModelRenderer::simulate(float deltaTime) {
_model->simulate(deltaTime);
}
void StaticModelRenderer::render(float alpha) {
void StaticModelRenderer::renderUnclipped(float alpha) {
_model->render(alpha);
}
bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const {
return _model->findRayIntersection(origin, direction, distance);
}

View file

@ -61,7 +61,7 @@ private:
SimulateVisitor(QVector<Point>& points);
void setDeltaTime(float deltaTime) { _deltaTime = deltaTime; }
void setOrder(const glm::vec3& direction) { _order = encodeOrder(direction); }
virtual bool visit(Spanner* spanner);
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
virtual int visit(MetavoxelInfo& info);
private:
@ -73,7 +73,7 @@ private:
class RenderVisitor : public SpannerVisitor {
public:
RenderVisitor();
virtual bool visit(Spanner* spanner);
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
};
static ProgramObject _program;
@ -141,19 +141,36 @@ private:
QList<ReceiveRecord> _receiveRecords;
};
/// Base class for spanner renderers; provides clipping.
class ClippedRenderer : public SpannerRenderer {
Q_OBJECT
public:
virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize);
protected:
virtual void renderUnclipped(float alpha) = 0;
};
/// Renders spheres.
class SphereRenderer : public SpannerRenderer {
class SphereRenderer : public ClippedRenderer {
Q_OBJECT
public:
Q_INVOKABLE SphereRenderer();
virtual void render(float alpha);
virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize);
protected:
virtual void renderUnclipped(float alpha);
};
/// Renders static models.
class StaticModelRenderer : public SpannerRenderer {
class StaticModelRenderer : public ClippedRenderer {
Q_OBJECT
public:
@ -162,8 +179,12 @@ public:
virtual void init(Spanner* spanner);
virtual void simulate(float deltaTime);
virtual void render(float alpha);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
protected:
virtual void renderUnclipped(float alpha);
private slots:

View file

@ -133,12 +133,17 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
setJointPosition(jointIndex, palm.getPosition());
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
int parentJointIndex = geometry.joints.at(jointIndex).parentIndex;
if (parentJointIndex == -1) {
return;
}
// rotate forearm to align with palm direction
glm::quat palmRotation;
getJointRotation(jointIndex, palmRotation, true);
applyRotationDelta(jointIndex, rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()), false);
getJointRotation(jointIndex, palmRotation, true);
getJointRotation(parentJointIndex, palmRotation, true);
applyRotationDelta(parentJointIndex, rotationBetween(palmRotation * geometry.palmDirection, palm.getNormal()), false);
getJointRotation(parentJointIndex, palmRotation, true);
// sort the finger indices by raw x, get the average direction
QVector<IndexValue> fingerIndices;
@ -155,33 +160,21 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
}
qSort(fingerIndices.begin(), fingerIndices.end());
// rotate palm according to average finger direction
// rotate forearm according to average finger direction
float directionLength = glm::length(direction);
const unsigned int MIN_ROTATION_FINGERS = 3;
if (directionLength > EPSILON && palm.getNumFingers() >= MIN_ROTATION_FINGERS) {
applyRotationDelta(jointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction), false);
getJointRotation(jointIndex, palmRotation, true);
applyRotationDelta(parentJointIndex, rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), direction), false);
getJointRotation(parentJointIndex, palmRotation, true);
}
// no point in continuing if there are no fingers
if (palm.getNumFingers() == 0 || fingerJointIndices.isEmpty()) {
return;
}
// let wrist inherit forearm rotation
_jointStates[jointIndex].rotation = glm::quat();
// match them up as best we can
float proportion = fingerIndices.size() / (float)fingerJointIndices.size();
for (int i = 0; i < fingerJointIndices.size(); i++) {
int fingerIndex = fingerIndices.at(roundf(i * proportion)).index;
glm::vec3 fingerVector = palm.getFingers()[fingerIndex].getTipPosition() -
palm.getFingers()[fingerIndex].getRootPosition();
int fingerJointIndex = fingerJointIndices.at(i);
int fingertipJointIndex = fingertipJointIndices.at(i);
glm::vec3 jointVector = extractTranslation(geometry.joints.at(fingertipJointIndex).bindTransform) -
extractTranslation(geometry.joints.at(fingerJointIndex).bindTransform);
setJointRotation(fingerJointIndex, rotationBetween(palmRotation * jointVector, fingerVector) * palmRotation, true);
}
// set elbow position from wrist position
glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f);
setJointPosition(parentJointIndex, palm.getPosition() + forearmVector *
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale));
}
void SkeletonModel::updateJointState(int index) {

View file

@ -565,7 +565,7 @@ void PlaceSpannerTool::render() {
}
Spanner* spanner = static_cast<Spanner*>(_editor->getValue().value<SharedObjectPointer>().data());
const float SPANNER_ALPHA = 0.25f;
spanner->getRenderer()->render(SPANNER_ALPHA);
spanner->getRenderer()->render(SPANNER_ALPHA, glm::vec3(), 0.0f);
}
bool PlaceSpannerTool::appliesTo(const AttributePointer& attribute) const {
@ -655,6 +655,5 @@ bool SetSpannerTool::appliesTo(const AttributePointer& attribute) const {
}
QVariant SetSpannerTool::createEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
static_cast<Spanner*>(spanner.data())->setGranularity(_editor->getGridSpacing());
return QVariant::fromValue(SetSpannerEdit(spanner));
}

View file

@ -16,6 +16,7 @@
#include "ImageOverlay.h"
ImageOverlay::ImageOverlay() :
_manager(0),
_textureID(0),
_renderImage(false),
_textureBound(false),
@ -33,9 +34,9 @@ ImageOverlay::~ImageOverlay() {
// TODO: handle setting image multiple times, how do we manage releasing the bound texture?
void ImageOverlay::setImageURL(const QUrl& url) {
// TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made?
QNetworkAccessManager* manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(url));
_manager = new QNetworkAccessManager();
connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
_manager->get(QNetworkRequest(url));
}
void ImageOverlay::replyFinished(QNetworkReply* reply) {
@ -44,7 +45,7 @@ void ImageOverlay::replyFinished(QNetworkReply* reply) {
QByteArray rawData = reply->readAll();
_textureImage.loadFromData(rawData);
_renderImage = true;
_manager->deleteLater();
}
void ImageOverlay::render() {

View file

@ -49,6 +49,8 @@ private:
QUrl _imageURL;
QImage _textureImage;
QNetworkAccessManager* _manager;
GLuint _textureID;
QRect _fromImage; // where from in the image to sample
bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle

View file

@ -1508,7 +1508,9 @@ void VoxelSystem::killLocalVoxels() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"VoxelSystem::killLocalVoxels()");
_tree->lockForWrite();
VoxelSystem* voxelSystem = _tree->getRoot()->getVoxelSystem();
_tree->eraseAllOctreeElements();
_tree->getRoot()->setVoxelSystem(voxelSystem);
_tree->unlock();
clearFreeBufferIndexes();
if (_usePrimitiveRenderer) {

View file

@ -11,12 +11,18 @@
#include "AttributeRegistry.h"
#include "MetavoxelData.h"
REGISTER_META_OBJECT(FloatAttribute)
REGISTER_META_OBJECT(QRgbAttribute)
REGISTER_META_OBJECT(PackedNormalAttribute)
REGISTER_META_OBJECT(SpannerQRgbAttribute)
REGISTER_META_OBJECT(SpannerPackedNormalAttribute)
REGISTER_META_OBJECT(SharedObjectAttribute)
REGISTER_META_OBJECT(SharedObjectSetAttribute)
REGISTER_META_OBJECT(SpannerSetAttribute)
static int attributePointerMetaTypeId = qRegisterMetaType<AttributePointer>();
static int ownedAttributeValueMetaTypeId = qRegisterMetaType<OwnedAttributeValue>();
AttributeRegistry* AttributeRegistry::getInstance() {
static AttributeRegistry registry;
return &registry;
@ -27,10 +33,13 @@ AttributeRegistry::AttributeRegistry() :
SharedObjectPointer(new DefaultMetavoxelGuide())))),
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))),
_colorAttribute(registerAttribute(new QRgbAttribute("color"))),
_normalAttribute(registerAttribute(new PackedNormalAttribute("normal", qRgb(0, 127, 0)))) {
_normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))),
_spannerColorAttribute(registerAttribute(new SpannerQRgbAttribute("spannerColor"))),
_spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))),
_spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))) {
// our baseline LOD threshold is for voxels; spanners are a different story
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 4.0f;
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f;
_spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER);
}
@ -127,6 +136,14 @@ OwnedAttributeValue::~OwnedAttributeValue() {
}
}
void OwnedAttributeValue::mix(const AttributeValue& first, const AttributeValue& second, float alpha) {
if (_attribute) {
_attribute->destroy(_value);
}
_attribute = first.getAttribute();
_value = _attribute->mix(first.getValue(), second.getValue(), alpha);
}
OwnedAttributeValue& OwnedAttributeValue::operator=(const AttributeValue& other) {
if (_attribute) {
_attribute->destroy(_value);
@ -155,6 +172,10 @@ Attribute::Attribute(const QString& name) :
Attribute::~Attribute() {
}
MetavoxelNode* Attribute::createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const {
return new MetavoxelNode(value);
}
void Attribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state) {
data.createRoot(state.attribute)->read(state);
}
@ -179,30 +200,49 @@ void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelSt
root.writeSubdivision(state);
}
FloatAttribute::FloatAttribute(const QString& name, float defaultValue) :
SimpleInlineAttribute<float>(name, defaultValue) {
}
QRgbAttribute::QRgbAttribute(const QString& name, QRgb defaultValue) :
InlineAttribute<QRgb>(name, defaultValue) {
}
bool QRgbAttribute::merge(void*& parent, void* children[]) const {
bool QRgbAttribute::merge(void*& parent, void* children[], bool postRead) const {
QRgb firstValue = decodeInline<QRgb>(children[0]);
int totalRed = qRed(firstValue);
int totalGreen = qGreen(firstValue);
int totalBlue = qBlue(firstValue);
int totalAlpha = qAlpha(firstValue);
int totalRed = qRed(firstValue) * totalAlpha;
int totalGreen = qGreen(firstValue) * totalAlpha;
int totalBlue = qBlue(firstValue) * totalAlpha;
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);
int alpha = qAlpha(value);
totalRed += qRed(value) * alpha;
totalGreen += qGreen(value) * alpha;
totalBlue += qBlue(value) * alpha;
totalAlpha += alpha;
allChildrenEqual &= (firstValue == value);
}
parent = encodeInline(qRgba(totalRed / MERGE_COUNT, totalGreen / MERGE_COUNT,
totalBlue / MERGE_COUNT, totalAlpha / MERGE_COUNT));
if (totalAlpha == 0) {
parent = encodeInline(QRgb());
} else {
parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha,
totalBlue / totalAlpha, totalAlpha / MERGE_COUNT));
}
return allChildrenEqual;
}
void* QRgbAttribute::mix(void* first, void* second, float alpha) const {
QRgb firstValue = decodeInline<QRgb>(first);
QRgb secondValue = decodeInline<QRgb>(second);
return encodeInline(qRgba(
glm::mix((float)qRed(firstValue), (float)qRed(secondValue), alpha),
glm::mix((float)qGreen(firstValue), (float)qGreen(secondValue), alpha),
glm::mix((float)qBlue(firstValue), (float)qBlue(secondValue), alpha),
glm::mix((float)qAlpha(firstValue), (float)qAlpha(secondValue), alpha)));
}
void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* engine) const {
return encodeInline((QRgb)value.toUInt32());
}
@ -227,23 +267,26 @@ PackedNormalAttribute::PackedNormalAttribute(const QString& name, QRgb defaultVa
QRgbAttribute(name, defaultValue) {
}
bool PackedNormalAttribute::merge(void*& parent, void* children[]) const {
bool PackedNormalAttribute::merge(void*& parent, void* children[], bool postRead) const {
QRgb firstValue = decodeInline<QRgb>(children[0]);
int totalRed = (char)qRed(firstValue);
int totalGreen = (char)qGreen(firstValue);
int totalBlue = (char)qBlue(firstValue);
glm::vec3 total = unpackNormal(firstValue) * (float)qAlpha(firstValue);
bool allChildrenEqual = true;
for (int i = 1; i < Attribute::MERGE_COUNT; i++) {
QRgb value = decodeInline<QRgb>(children[i]);
totalRed += (char)qRed(value);
totalGreen += (char)qGreen(value);
totalBlue += (char)qBlue(value);
total += unpackNormal(value) * (float)qAlpha(value);
allChildrenEqual &= (firstValue == value);
}
parent = encodeInline(packNormal(glm::normalize(glm::vec3(totalRed, totalGreen, totalBlue))));
float length = glm::length(total);
parent = encodeInline(length < EPSILON ? QRgb() : packNormal(total / length));
return allChildrenEqual;
}
void* PackedNormalAttribute::mix(void* first, void* second, float alpha) const {
glm::vec3 firstNormal = unpackNormal(decodeInline<QRgb>(first));
glm::vec3 secondNormal = unpackNormal(decodeInline<QRgb>(second));
return encodeInline(packNormal(glm::normalize(glm::mix(firstNormal, secondNormal, alpha))));
}
const float CHAR_SCALE = 127.0f;
const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE;
@ -256,6 +299,106 @@ glm::vec3 unpackNormal(QRgb value) {
(char)qBlue(value) * INVERSE_CHAR_SCALE);
}
SpannerQRgbAttribute::SpannerQRgbAttribute(const QString& name, QRgb defaultValue) :
QRgbAttribute(name, defaultValue) {
}
void SpannerQRgbAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
value = getDefaultValue();
in.read(&value, 32);
}
void SpannerQRgbAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
out.write(&value, 32);
}
MetavoxelNode* SpannerQRgbAttribute::createMetavoxelNode(
const AttributeValue& value, const MetavoxelNode* original) const {
return new MetavoxelNode(value, original);
}
bool SpannerQRgbAttribute::merge(void*& parent, void* children[], bool postRead) const {
if (postRead) {
for (int i = 0; i < MERGE_COUNT; i++) {
if (qAlpha(decodeInline<QRgb>(children[i])) != 0) {
return false;
}
}
return true;
}
QRgb parentValue = decodeInline<QRgb>(parent);
int totalAlpha = qAlpha(parentValue) * Attribute::MERGE_COUNT;
int totalRed = qRed(parentValue) * totalAlpha;
int totalGreen = qGreen(parentValue) * totalAlpha;
int totalBlue = qBlue(parentValue) * totalAlpha;
bool allChildrenTransparent = true;
for (int i = 0; i < Attribute::MERGE_COUNT; i++) {
QRgb value = decodeInline<QRgb>(children[i]);
int alpha = qAlpha(value);
totalRed += qRed(value) * alpha;
totalGreen += qGreen(value) * alpha;
totalBlue += qBlue(value) * alpha;
totalAlpha += alpha;
allChildrenTransparent &= (alpha == 0);
}
if (totalAlpha == 0) {
parent = encodeInline(QRgb());
} else {
parent = encodeInline(qRgba(totalRed / totalAlpha, totalGreen / totalAlpha,
totalBlue / totalAlpha, totalAlpha / MERGE_COUNT));
}
return allChildrenTransparent;
}
AttributeValue SpannerQRgbAttribute::inherit(const AttributeValue& parentValue) const {
return AttributeValue(parentValue.getAttribute());
}
SpannerPackedNormalAttribute::SpannerPackedNormalAttribute(const QString& name, QRgb defaultValue) :
PackedNormalAttribute(name, defaultValue) {
}
void SpannerPackedNormalAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
value = getDefaultValue();
in.read(&value, 32);
}
void SpannerPackedNormalAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
out.write(&value, 32);
}
MetavoxelNode* SpannerPackedNormalAttribute::createMetavoxelNode(
const AttributeValue& value, const MetavoxelNode* original) const {
return new MetavoxelNode(value, original);
}
bool SpannerPackedNormalAttribute::merge(void*& parent, void* children[], bool postRead) const {
if (postRead) {
for (int i = 0; i < MERGE_COUNT; i++) {
if (qAlpha(decodeInline<QRgb>(children[i])) != 0) {
return false;
}
}
return true;
}
QRgb parentValue = decodeInline<QRgb>(parent);
glm::vec3 total = unpackNormal(parentValue) * (float)(qAlpha(parentValue) * Attribute::MERGE_COUNT);
bool allChildrenTransparent = true;
for (int i = 0; i < Attribute::MERGE_COUNT; i++) {
QRgb value = decodeInline<QRgb>(children[i]);
int alpha = qAlpha(value);
total += unpackNormal(value) * (float)alpha;
allChildrenTransparent &= (alpha == 0);
}
float length = glm::length(total);
parent = encodeInline(length < EPSILON ? QRgb() : packNormal(total / length));
return allChildrenTransparent;
}
AttributeValue SpannerPackedNormalAttribute::inherit(const AttributeValue& parentValue) const {
return AttributeValue(parentValue.getAttribute());
}
SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject,
const SharedObjectPointer& defaultValue) :
InlineAttribute<SharedObjectPointer>(name, defaultValue),
@ -275,7 +418,7 @@ void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) cons
}
}
bool SharedObjectAttribute::merge(void*& parent, void* children[]) const {
bool SharedObjectAttribute::merge(void*& parent, void* children[], bool postRead) const {
SharedObjectPointer firstChild = decodeInline<SharedObjectPointer>(children[0]);
for (int i = 1; i < MERGE_COUNT; i++) {
if (firstChild != decodeInline<SharedObjectPointer>(children[i])) {
@ -310,7 +453,12 @@ void SharedObjectSetAttribute::write(Bitstream& out, void* value, bool isLeaf) c
out << decodeInline<SharedObjectSet>(value);
}
bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const {
MetavoxelNode* SharedObjectSetAttribute::createMetavoxelNode(
const AttributeValue& value, const MetavoxelNode* original) const {
return new MetavoxelNode(value, original);
}
bool SharedObjectSetAttribute::merge(void*& parent, void* children[], bool postRead) const {
for (int i = 0; i < MERGE_COUNT; i++) {
if (!decodeInline<SharedObjectSet>(children[i]).isEmpty()) {
return false;
@ -319,6 +467,10 @@ bool SharedObjectSetAttribute::merge(void*& parent, void* children[]) const {
return true;
}
AttributeValue SharedObjectSetAttribute::inherit(const AttributeValue& parentValue) const {
return AttributeValue(parentValue.getAttribute());
}
QWidget* SharedObjectSetAttribute::createEditor(QWidget* parent) const {
return new SharedObjectEditor(_metaObject, parent);
}

View file

@ -29,6 +29,8 @@ class MetavoxelStreamState;
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
Q_DECLARE_METATYPE(AttributePointer)
/// Maintains information about metavoxel attribute types.
class AttributeRegistry {
public:
@ -69,9 +71,18 @@ public:
/// Returns a reference to the standard QRgb "color" attribute.
const AttributePointer& getColorAttribute() const { return _colorAttribute; }
/// Returns a reference to the standard QRgb "normal" attribute.
/// Returns a reference to the standard packed normal "normal" attribute.
const AttributePointer& getNormalAttribute() const { return _normalAttribute; }
/// Returns a reference to the standard QRgb "spannerColor" attribute.
const AttributePointer& getSpannerColorAttribute() const { return _spannerColorAttribute; }
/// Returns a reference to the standard packed normal "spannerNormal" attribute.
const AttributePointer& getSpannerNormalAttribute() const { return _spannerNormalAttribute; }
/// Returns a reference to the standard "spannerMask" attribute.
const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; }
private:
static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine);
@ -81,6 +92,9 @@ private:
AttributePointer _spannersAttribute;
AttributePointer _colorAttribute;
AttributePointer _normalAttribute;
AttributePointer _spannerColorAttribute;
AttributePointer _spannerNormalAttribute;
AttributePointer _spannerMaskAttribute;
};
/// Converts a value to a void pointer.
@ -106,8 +120,6 @@ public:
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;
@ -143,6 +155,9 @@ public:
/// Destroys the current value, if any.
~OwnedAttributeValue();
/// Sets this attribute to a mix of the first and second provided.
void mix(const AttributeValue& first, const AttributeValue& second, float alpha);
/// Destroys the current value, if any, and copies the specified other value.
OwnedAttributeValue& operator=(const AttributeValue& other);
@ -150,6 +165,8 @@ public:
OwnedAttributeValue& operator=(const OwnedAttributeValue& other);
};
Q_DECLARE_METATYPE(OwnedAttributeValue)
/// Represents a registered attribute.
class Attribute : public SharedObject {
Q_OBJECT
@ -177,6 +194,8 @@ public:
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const { read(in, value, isLeaf); }
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const { write(out, value, isLeaf); }
virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const;
virtual void readMetavoxelRoot(MetavoxelData& data, MetavoxelStreamState& state);
virtual void writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state);
@ -189,8 +208,15 @@ public:
virtual bool equal(void* first, void* second) const = 0;
/// Merges the value of a parent and its children.
/// \param postRead whether or not the merge is happening after a read
/// \return whether or not the children and parent values are all equal
virtual bool merge(void*& parent, void* children[]) const = 0;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const = 0;
/// Given the parent value, returns the value that children should inherit (either the parent value or the default).
virtual AttributeValue inherit(const AttributeValue& parentValue) const { return parentValue; }
/// Mixes the first and the second, returning a new value with the result.
virtual void* mix(void* first, void* second, float alpha) const = 0;
virtual void* getDefaultValue() const = 0;
@ -221,6 +247,8 @@ public:
virtual bool equal(void* first, void* second) const { return decodeInline<T>(first) == decodeInline<T>(second); }
virtual void* mix(void* first, void* second, float alpha) const { return create(alpha < 0.5f ? first : second); }
virtual void* getDefaultValue() const { return encodeInline(_defaultValue); }
protected:
@ -241,27 +269,39 @@ template<class T, int bits> inline void InlineAttribute<T, bits>::write(Bitstrea
}
}
/// Provides merging using the =, ==, += and /= operators.
/// Provides averaging 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) { }
SimpleInlineAttribute(const QString& name, const T& defaultValue = T()) : InlineAttribute<T, bits>(name, defaultValue) { }
virtual bool merge(void*& parent, void* children[]) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) 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]);
template<class T, int bits> inline bool SimpleInlineAttribute<T, bits>::merge(
void*& parent, void* children[], bool postRead) const {
T firstValue = decodeInline<T>(children[0]);
T totalValue = firstValue;
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]));
T value = decodeInline<T>(children[i]);
totalValue += value;
allChildrenEqual &= (firstValue == value);
}
merged /= Attribute::MERGE_COUNT;
parent = encodeInline(totalValue / Attribute::MERGE_COUNT);
return allChildrenEqual;
}
/// Simple float attribute.
class FloatAttribute : public SimpleInlineAttribute<float> {
Q_OBJECT
Q_PROPERTY(float defaultValue MEMBER _defaultValue)
public:
Q_INVOKABLE FloatAttribute(const QString& name = QString(), float defaultValue = 0.0f);
};
/// Provides appropriate averaging for RGBA values.
class QRgbAttribute : public InlineAttribute<QRgb> {
Q_OBJECT
@ -271,7 +311,9 @@ public:
Q_INVOKABLE QRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb());
virtual bool merge(void*& parent, void* children[]) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual void* mix(void* first, void* second, float alpha) const;
virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const;
@ -288,7 +330,9 @@ public:
Q_INVOKABLE PackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb());
virtual bool merge(void*& parent, void* children[]) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual void* mix(void* first, void* second, float alpha) const;
};
/// Packs a normal into an RGB value.
@ -297,6 +341,42 @@ QRgb packNormal(const glm::vec3& normal);
/// Unpacks a normal from an RGB value.
glm::vec3 unpackNormal(QRgb value);
/// RGBA values for voxelized spanners.
class SpannerQRgbAttribute : public QRgbAttribute {
Q_OBJECT
public:
Q_INVOKABLE SpannerQRgbAttribute(const QString& name = QString(), QRgb defaultValue = QRgb());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
/// Packed normals for voxelized spanners.
class SpannerPackedNormalAttribute : public PackedNormalAttribute {
Q_OBJECT
public:
Q_INVOKABLE SpannerPackedNormalAttribute(const QString& name = QString(), QRgb defaultValue = QRgb());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
/// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject).
class SharedObjectAttribute : public InlineAttribute<SharedObjectPointer> {
Q_OBJECT
@ -311,7 +391,7 @@ public:
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[]) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual void* createFromVariant(const QVariant& value) const;
@ -337,7 +417,11 @@ public:
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[]) const;
virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
virtual QWidget* createEditor(QWidget* parent = NULL) const;

View file

@ -8,8 +8,8 @@
#include <cstring>
#include <QCryptographicHash>
#include <QDataStream>
#include <QMetaProperty>
#include <QMetaType>
#include <QUrl>
#include <QtDebug>
@ -31,6 +31,7 @@ REGISTER_SIMPLE_TYPE_STREAMER(QString)
REGISTER_SIMPLE_TYPE_STREAMER(QUrl)
REGISTER_SIMPLE_TYPE_STREAMER(QVariantList)
REGISTER_SIMPLE_TYPE_STREAMER(QVariantHash)
REGISTER_SIMPLE_TYPE_STREAMER(SharedObjectPointer)
// some types don't quite work with our macro
static int vec3Streamer = Bitstream::registerTypeStreamer(qMetaTypeId<glm::vec3>(), new SimpleTypeStreamer<glm::vec3>());
@ -82,6 +83,10 @@ int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) {
return 0;
}
const TypeStreamer* Bitstream::getTypeStreamer(int type) {
return getTypeStreamers().value(type);
}
const QMetaObject* Bitstream::getMetaObject(const QByteArray& className) {
return getMetaObjects().value(className);
}
@ -90,11 +95,12 @@ QList<const QMetaObject*> Bitstream::getMetaObjectSubClasses(const QMetaObject*
return getMetaObjectSubClasses().values(metaObject);
}
Bitstream::Bitstream(QDataStream& underlying, QObject* parent) :
Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, QObject* parent) :
QObject(parent),
_underlying(underlying),
_byte(0),
_position(0),
_metadataType(metadataType),
_metaObjectStreamer(*this),
_typeStreamerStreamer(*this),
_attributeStreamer(*this),
@ -102,6 +108,14 @@ Bitstream::Bitstream(QDataStream& underlying, QObject* parent) :
_sharedObjectStreamer(*this) {
}
void Bitstream::addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject) {
_metaObjectSubstitutions.insert(className, metaObject);
}
void Bitstream::addTypeSubstitution(const QByteArray& typeName, int type) {
_typeStreamerSubstitutions.insert(typeName, getTypeStreamers().value(type));
}
const int LAST_BIT_POSITION = BITS_IN_BYTE - 1;
Bitstream& Bitstream::write(const void* data, int bits, int offset) {
@ -171,9 +185,16 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) {
// find out when shared objects are deleted in order to clear their mappings
for (QHash<SharedObjectPointer, int>::const_iterator it = mappings.sharedObjectOffsets.constBegin();
it != mappings.sharedObjectOffsets.constEnd(); it++) {
if (it.key()) {
connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*)));
if (!it.key()) {
continue;
}
connect(it.key().data(), SIGNAL(destroyed(QObject*)), SLOT(clearSharedObject(QObject*)));
QPointer<SharedObject>& reference = _sharedObjectReferences[it.key()->getID()];
if (reference) {
_sharedObjectStreamer.removePersistentID(reference);
reference->disconnect(this);
}
reference = it.key();
}
}
@ -196,6 +217,19 @@ void Bitstream::persistReadMappings(const ReadMappings& mappings) {
_attributeStreamer.persistTransientValues(mappings.attributeValues);
_scriptStringStreamer.persistTransientValues(mappings.scriptStringValues);
_sharedObjectStreamer.persistTransientValues(mappings.sharedObjectValues);
for (QHash<int, SharedObjectPointer>::const_iterator it = mappings.sharedObjectValues.constBegin();
it != mappings.sharedObjectValues.constEnd(); it++) {
if (!it.value()) {
continue;
}
QPointer<SharedObject>& reference = _sharedObjectReferences[it.value()->getRemoteID()];
if (reference) {
_sharedObjectStreamer.removePersistentValue(reference.data());
}
reference = it.value();
_weakSharedObjectHash.remove(it.value()->getRemoteID());
}
}
void Bitstream::persistAndResetReadMappings() {
@ -209,6 +243,58 @@ void Bitstream::clearSharedObject(int id) {
}
}
void Bitstream::writeDelta(bool value, bool reference) {
*this << value;
}
void Bitstream::readDelta(bool& value, bool reference) {
*this >> value;
}
void Bitstream::writeDelta(const QVariant& value, const QVariant& reference) {
// QVariant only handles == for built-in types; we need to use our custom operators
const TypeStreamer* streamer = getTypeStreamers().value(value.userType());
if (value.userType() == reference.userType() && (!streamer || streamer->equal(value, reference))) {
*this << false;
return;
}
*this << true;
_typeStreamerStreamer << streamer;
streamer->writeRawDelta(*this, value, reference);
}
void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) {
TypeReader typeReader;
_typeStreamerStreamer >> typeReader;
typeReader.readRawDelta(*this, value, reference);
}
void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) {
if (!value) {
_metaObjectStreamer << NULL;
return;
}
const QMetaObject* metaObject = value->metaObject();
_metaObjectStreamer << metaObject;
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (!property.isStored(value)) {
continue;
}
const TypeStreamer* streamer = getTypeStreamers().value(property.userType());
if (streamer) {
streamer->writeDelta(*this, property.read(value), reference && metaObject == reference->metaObject() ?
property.read(reference) : QVariant());
}
}
}
void Bitstream::readRawDelta(QObject*& value, const QObject* reference) {
ObjectReader objectReader;
_metaObjectStreamer >> objectReader;
value = objectReader.readDelta(*this, reference);
}
Bitstream& Bitstream::operator<<(bool value) {
if (value) {
_byte |= (1 << _position);
@ -324,11 +410,9 @@ Bitstream& Bitstream::operator<<(const QVariant& value) {
}
Bitstream& Bitstream::operator>>(QVariant& value) {
const TypeStreamer* streamer;
_typeStreamerStreamer >> streamer;
if (streamer) {
value = streamer->read(*this);
}
TypeReader reader;
_typeStreamerStreamer >> reader;
value = reader.read(*this);
return *this;
}
@ -376,13 +460,9 @@ Bitstream& Bitstream::operator<<(const QObject* object) {
}
Bitstream& Bitstream::operator>>(QObject*& object) {
const QMetaObject* metaObject;
_metaObjectStreamer >> metaObject;
if (!metaObject) {
object = NULL;
return *this;
}
readProperties(object = metaObject->newInstance());
ObjectReader objectReader;
_metaObjectStreamer >> objectReader;
object = objectReader.read(*this);
return *this;
}
@ -392,7 +472,14 @@ Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) {
}
Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) {
_metaObjectStreamer >> metaObject;
ObjectReader objectReader;
_metaObjectStreamer >> objectReader;
metaObject = objectReader.getMetaObject();
return *this;
}
Bitstream& Bitstream::operator>>(ObjectReader& objectReader) {
_metaObjectStreamer >> objectReader;
return *this;
}
@ -402,7 +489,14 @@ Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) {
}
Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) {
_typeStreamerStreamer >> streamer;
TypeReader typeReader;
_typeStreamerStreamer >> typeReader;
streamer = typeReader.getStreamer();
return *this;
}
Bitstream& Bitstream::operator>>(TypeReader& reader) {
_typeStreamerStreamer >> reader;
return *this;
}
@ -437,36 +531,280 @@ Bitstream& Bitstream::operator>>(SharedObjectPointer& object) {
}
Bitstream& Bitstream::operator<(const QMetaObject* metaObject) {
return *this << (metaObject ? QByteArray::fromRawData(metaObject->className(),
strlen(metaObject->className())) : QByteArray());
if (!metaObject) {
return *this << QByteArray();
}
*this << QByteArray::fromRawData(metaObject->className(), strlen(metaObject->className()));
if (_metadataType == NO_METADATA) {
return *this;
}
int storedPropertyCount = 0;
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (property.isStored() && getTypeStreamers().contains(property.userType())) {
storedPropertyCount++;
}
}
*this << storedPropertyCount;
QCryptographicHash hash(QCryptographicHash::Md5);
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (!property.isStored()) {
continue;
}
const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType());
if (!typeStreamer) {
continue;
}
_typeStreamerStreamer << typeStreamer;
if (_metadataType == FULL_METADATA) {
*this << QByteArray::fromRawData(property.name(), strlen(property.name()));
} else {
hash.addData(property.name(), strlen(property.name()) + 1);
}
}
if (_metadataType == HASH_METADATA) {
QByteArray hashResult = hash.result();
write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE);
}
return *this;
}
Bitstream& Bitstream::operator>(const QMetaObject*& metaObject) {
Bitstream& Bitstream::operator>(ObjectReader& objectReader) {
QByteArray className;
*this >> className;
if (className.isEmpty()) {
metaObject = NULL;
objectReader = ObjectReader();
return *this;
}
metaObject = getMetaObjects().value(className);
const QMetaObject* metaObject = _metaObjectSubstitutions.value(className);
if (!metaObject) {
metaObject = getMetaObjects().value(className);
}
if (!metaObject) {
qWarning() << "Unknown class name: " << className << "\n";
}
if (_metadataType == NO_METADATA) {
objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject));
return *this;
}
int storedPropertyCount;
*this >> storedPropertyCount;
QVector<PropertyReader> properties(storedPropertyCount);
for (int i = 0; i < storedPropertyCount; i++) {
TypeReader typeReader;
*this >> typeReader;
QMetaProperty property = QMetaProperty();
if (_metadataType == FULL_METADATA) {
QByteArray propertyName;
*this >> propertyName;
if (metaObject) {
property = metaObject->property(metaObject->indexOfProperty(propertyName));
}
}
properties[i] = PropertyReader(typeReader, property);
}
// for hash metadata, check the names/types of the properties as well as the name hash against our own class
if (_metadataType == HASH_METADATA) {
QCryptographicHash hash(QCryptographicHash::Md5);
bool matches = true;
if (metaObject) {
int propertyIndex = 0;
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (!property.isStored()) {
continue;
}
const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType());
if (!typeStreamer) {
continue;
}
if (propertyIndex >= properties.size() ||
!properties.at(propertyIndex).getReader().matchesExactly(typeStreamer)) {
matches = false;
break;
}
hash.addData(property.name(), strlen(property.name()) + 1);
propertyIndex++;
}
if (propertyIndex != properties.size()) {
matches = false;
}
}
QByteArray localHashResult = hash.result();
QByteArray remoteHashResult(localHashResult.size(), 0);
read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE);
if (metaObject && matches && localHashResult == remoteHashResult) {
objectReader = ObjectReader(className, metaObject, getPropertyReaders(metaObject));
return *this;
}
}
objectReader = ObjectReader(className, metaObject, properties);
return *this;
}
Bitstream& Bitstream::operator<(const TypeStreamer* streamer) {
const char* typeName = QMetaType::typeName(streamer->getType());
return *this << QByteArray::fromRawData(typeName, strlen(typeName));
*this << QByteArray::fromRawData(typeName, strlen(typeName));
if (_metadataType == NO_METADATA) {
return *this;
}
TypeReader::Type type = streamer->getReaderType();
*this << (int)type;
switch (type) {
case TypeReader::SIMPLE_TYPE:
return *this;
case TypeReader::LIST_TYPE:
case TypeReader::SET_TYPE:
return *this << streamer->getValueStreamer();
case TypeReader::MAP_TYPE:
return *this << streamer->getKeyStreamer() << streamer->getValueStreamer();
default:
break; // fall through
}
// streamable type
const QVector<MetaField>& metaFields = streamer->getMetaFields();
*this << metaFields.size();
if (metaFields.isEmpty()) {
return *this;
}
QCryptographicHash hash(QCryptographicHash::Md5);
foreach (const MetaField& metaField, metaFields) {
_typeStreamerStreamer << metaField.getStreamer();
if (_metadataType == FULL_METADATA) {
*this << metaField.getName();
} else {
hash.addData(metaField.getName().constData(), metaField.getName().size() + 1);
}
}
if (_metadataType == HASH_METADATA) {
QByteArray hashResult = hash.result();
write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE);
}
return *this;
}
Bitstream& Bitstream::operator>(const TypeStreamer*& streamer) {
Bitstream& Bitstream::operator>(TypeReader& reader) {
QByteArray typeName;
*this >> typeName;
streamer = getTypeStreamers().value(QMetaType::type(typeName.constData()));
const TypeStreamer* streamer = _typeStreamerSubstitutions.value(typeName);
if (!streamer) {
streamer = getTypeStreamers().value(QMetaType::type(typeName.constData()));
}
if (!streamer) {
qWarning() << "Unknown type name: " << typeName << "\n";
}
if (_metadataType == NO_METADATA) {
reader = TypeReader(typeName, streamer);
return *this;
}
int type;
*this >> type;
switch (type) {
case TypeReader::SIMPLE_TYPE:
reader = TypeReader(typeName, streamer);
return *this;
case TypeReader::LIST_TYPE:
case TypeReader::SET_TYPE: {
TypeReader valueReader;
*this >> valueReader;
if (streamer && streamer->getReaderType() == type &&
valueReader.matchesExactly(streamer->getValueStreamer())) {
reader = TypeReader(typeName, streamer);
} else {
reader = TypeReader(typeName, streamer, false, (TypeReader::Type)type, TypeReaderPointer(),
TypeReaderPointer(new TypeReader(valueReader)));
}
return *this;
}
case TypeReader::MAP_TYPE: {
TypeReader keyReader, valueReader;
*this >> keyReader >> valueReader;
if (streamer && streamer->getReaderType() == TypeReader::MAP_TYPE &&
keyReader.matchesExactly(streamer->getKeyStreamer()) &&
valueReader.matchesExactly(streamer->getValueStreamer())) {
reader = TypeReader(typeName, streamer);
} else {
reader = TypeReader(typeName, streamer, false, TypeReader::MAP_TYPE,
TypeReaderPointer(new TypeReader(keyReader)), TypeReaderPointer(new TypeReader(valueReader)));
}
return *this;
}
}
// streamable type
int fieldCount;
*this >> fieldCount;
QVector<FieldReader> fields(fieldCount);
for (int i = 0; i < fieldCount; i++) {
TypeReader typeReader;
*this >> typeReader;
int index = -1;
if (_metadataType == FULL_METADATA) {
QByteArray fieldName;
*this >> fieldName;
if (streamer) {
index = streamer->getFieldIndex(fieldName);
}
}
fields[i] = FieldReader(typeReader, index);
}
// for hash metadata, check the names/types of the fields as well as the name hash against our own class
if (_metadataType == HASH_METADATA) {
QCryptographicHash hash(QCryptographicHash::Md5);
bool matches = true;
if (streamer) {
const QVector<MetaField>& localFields = streamer->getMetaFields();
if (fieldCount != localFields.size()) {
matches = false;
} else {
if (fieldCount == 0) {
reader = TypeReader(typeName, streamer);
return *this;
}
for (int i = 0; i < fieldCount; i++) {
const MetaField& localField = localFields.at(i);
if (!fields.at(i).getReader().matchesExactly(localField.getStreamer())) {
matches = false;
break;
}
hash.addData(localField.getName().constData(), localField.getName().size() + 1);
}
}
}
QByteArray localHashResult = hash.result();
QByteArray remoteHashResult(localHashResult.size(), 0);
read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE);
if (streamer && matches && localHashResult == remoteHashResult) {
// since everything is the same, we can use the default streamer
reader = TypeReader(typeName, streamer);
return *this;
}
} else if (streamer) {
// if all fields are the same type and in the right order, we can use the (more efficient) default streamer
const QVector<MetaField>& localFields = streamer->getMetaFields();
if (fieldCount != localFields.size()) {
reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE,
TypeReaderPointer(), TypeReaderPointer(), fields);
return *this;
}
for (int i = 0; i < fieldCount; i++) {
const FieldReader& fieldReader = fields.at(i);
if (!fieldReader.getReader().matchesExactly(localFields.at(i).getStreamer()) || fieldReader.getIndex() != i) {
reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE,
TypeReaderPointer(), TypeReaderPointer(), fields);
return *this;
}
}
reader = TypeReader(typeName, streamer);
return *this;
}
reader = TypeReader(typeName, streamer, false, TypeReader::STREAMABLE_TYPE,
TypeReaderPointer(), TypeReaderPointer(), fields);
return *this;
}
@ -496,7 +834,14 @@ Bitstream& Bitstream::operator<(const SharedObjectPointer& object) {
if (!object) {
return *this << (int)0;
}
return *this << object->getID() << (QObject*)object.data();
*this << object->getID();
QPointer<SharedObject> reference = _sharedObjectReferences.value(object->getID());
if (reference) {
writeRawDelta((QObject*)object.data(), (QObject*)reference.data());
} else {
*this << (QObject*)object.data();
}
return *this;
}
Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
@ -506,18 +851,23 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
object = SharedObjectPointer();
return *this;
}
QPointer<SharedObject> reference = _sharedObjectReferences.value(id);
QPointer<SharedObject>& pointer = _weakSharedObjectHash[id];
if (pointer) {
const QMetaObject* metaObject;
_metaObjectStreamer >> metaObject;
if (metaObject != pointer->metaObject()) {
qWarning() << "Class mismatch: " << pointer->metaObject()->className() << metaObject->className();
ObjectReader objectReader;
_metaObjectStreamer >> objectReader;
if (reference) {
objectReader.readDelta(*this, reference.data(), pointer.data());
} else {
objectReader.read(*this, pointer.data());
}
readProperties(pointer.data());
} else {
QObject* rawObject;
*this >> rawObject;
QObject* rawObject;
if (reference) {
readRawDelta(rawObject, (QObject*)reference.data());
} else {
*this >> rawObject;
}
pointer = static_cast<SharedObject*>(rawObject);
pointer->setRemoteID(id);
}
@ -526,26 +876,14 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
}
void Bitstream::clearSharedObject(QObject* object) {
int id = _sharedObjectStreamer.takePersistentID(static_cast<SharedObject*>(object));
SharedObject* sharedObject = static_cast<SharedObject*>(object);
_sharedObjectReferences.remove(sharedObject->getID());
int id = _sharedObjectStreamer.takePersistentID(sharedObject);
if (id != 0) {
emit sharedObjectCleared(id);
}
}
void Bitstream::readProperties(QObject* object) {
const QMetaObject* metaObject = object->metaObject();
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (!property.isStored(object)) {
continue;
}
const TypeStreamer* streamer = getTypeStreamers().value(property.userType());
if (streamer) {
property.write(object, streamer->read(*this));
}
}
}
QHash<QByteArray, const QMetaObject*>& Bitstream::getMetaObjects() {
static QHash<QByteArray, const QMetaObject*> metaObjects;
return metaObjects;
@ -561,3 +899,324 @@ QHash<int, const TypeStreamer*>& Bitstream::getTypeStreamers() {
return typeStreamers;
}
QVector<PropertyReader> Bitstream::getPropertyReaders(const QMetaObject* metaObject) {
QVector<PropertyReader> propertyReaders;
if (!metaObject) {
return propertyReaders;
}
for (int i = 0; i < metaObject->propertyCount(); i++) {
QMetaProperty property = metaObject->property(i);
if (!property.isStored()) {
continue;
}
const TypeStreamer* typeStreamer = getTypeStreamers().value(property.userType());
if (typeStreamer) {
propertyReaders.append(PropertyReader(TypeReader(QByteArray(), typeStreamer), property));
}
}
return propertyReaders;
}
TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, bool exactMatch, Type type,
const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader, const QVector<FieldReader>& fields) :
_typeName(typeName),
_streamer(streamer),
_exactMatch(exactMatch),
_type(type),
_keyReader(keyReader),
_valueReader(valueReader),
_fields(fields) {
}
QVariant TypeReader::read(Bitstream& in) const {
if (_exactMatch) {
return _streamer->read(in);
}
QVariant object = _streamer ? QVariant(_streamer->getType(), 0) : QVariant();
switch (_type) {
case STREAMABLE_TYPE: {
foreach (const FieldReader& field, _fields) {
field.read(in, _streamer, object);
}
break;
}
case LIST_TYPE:
case SET_TYPE: {
int size;
in >> size;
for (int i = 0; i < size; i++) {
QVariant value = _valueReader->read(in);
if (_streamer) {
_streamer->insert(object, value);
}
}
break;
}
case MAP_TYPE: {
int size;
in >> size;
for (int i = 0; i < size; i++) {
QVariant key = _keyReader->read(in);
QVariant value = _valueReader->read(in);
if (_streamer) {
_streamer->insert(object, key, value);
}
}
break;
}
default:
break;
}
return object;
}
void TypeReader::readDelta(Bitstream& in, QVariant& object, const QVariant& reference) const {
if (_exactMatch) {
_streamer->readDelta(in, object, reference);
return;
}
bool changed;
in >> changed;
if (changed) {
readRawDelta(in, object, reference);
} else {
object = reference;
}
}
void TypeReader::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const {
if (_exactMatch) {
_streamer->readRawDelta(in, object, reference);
return;
}
switch (_type) {
case STREAMABLE_TYPE: {
foreach (const FieldReader& field, _fields) {
field.readDelta(in, _streamer, object, reference);
}
break;
}
case LIST_TYPE: {
object = reference;
int size, referenceSize;
in >> size >> referenceSize;
if (_streamer) {
if (size < referenceSize) {
_streamer->prune(object, size);
}
for (int i = 0; i < size; i++) {
if (i < referenceSize) {
QVariant value;
_valueReader->readDelta(in, value, _streamer->getValue(reference, i));
_streamer->setValue(object, i, value);
} else {
_streamer->insert(object, _valueReader->read(in));
}
}
} else {
for (int i = 0; i < size; i++) {
if (i < referenceSize) {
QVariant value;
_valueReader->readDelta(in, value, QVariant());
} else {
_valueReader->read(in);
}
}
}
break;
}
case SET_TYPE: {
object = reference;
int addedOrRemoved;
in >> addedOrRemoved;
for (int i = 0; i < addedOrRemoved; i++) {
QVariant value = _valueReader->read(in);
if (_streamer && !_streamer->remove(object, value)) {
_streamer->insert(object, value);
}
}
break;
}
case MAP_TYPE: {
object = reference;
int added;
in >> added;
for (int i = 0; i < added; i++) {
QVariant key = _keyReader->read(in);
QVariant value = _valueReader->read(in);
if (_streamer) {
_streamer->insert(object, key, value);
}
}
int modified;
in >> modified;
for (int i = 0; i < modified; i++) {
QVariant key = _keyReader->read(in);
QVariant value;
if (_streamer) {
_valueReader->readDelta(in, value, _streamer->getValue(reference, key));
_streamer->insert(object, key, value);
} else {
_valueReader->readDelta(in, value, QVariant());
}
}
int removed;
in >> removed;
for (int i = 0; i < removed; i++) {
QVariant key = _keyReader->read(in);
if (_streamer) {
_streamer->remove(object, key);
}
}
break;
}
default:
break;
}
}
bool TypeReader::matchesExactly(const TypeStreamer* streamer) const {
return _exactMatch && _streamer == streamer;
}
uint qHash(const TypeReader& typeReader, uint seed) {
return qHash(typeReader.getTypeName(), seed);
}
FieldReader::FieldReader(const TypeReader& reader, int index) :
_reader(reader),
_index(index) {
}
void FieldReader::read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const {
QVariant value = _reader.read(in);
if (_index != -1 && streamer) {
streamer->setField(object, _index, value);
}
}
void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVariant& object, const QVariant& reference) const {
QVariant value;
if (_index != -1 && streamer) {
_reader.readDelta(in, value, streamer->getField(reference, _index));
streamer->setField(object, _index, value);
} else {
_reader.readDelta(in, value, QVariant());
}
}
ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject,
const QVector<PropertyReader>& properties) :
_className(className),
_metaObject(metaObject),
_properties(properties) {
}
QObject* ObjectReader::read(Bitstream& in, QObject* object) const {
if (!object && _metaObject) {
object = _metaObject->newInstance();
}
foreach (const PropertyReader& property, _properties) {
property.read(in, object);
}
return object;
}
QObject* ObjectReader::readDelta(Bitstream& in, const QObject* reference, QObject* object) const {
if (!object && _metaObject) {
object = _metaObject->newInstance();
}
foreach (const PropertyReader& property, _properties) {
property.readDelta(in, object, reference);
}
return object;
}
uint qHash(const ObjectReader& objectReader, uint seed) {
return qHash(objectReader.getClassName(), seed);
}
PropertyReader::PropertyReader(const TypeReader& reader, const QMetaProperty& property) :
_reader(reader),
_property(property) {
}
void PropertyReader::read(Bitstream& in, QObject* object) const {
QVariant value = _reader.read(in);
if (_property.isValid() && object) {
_property.write(object, value);
}
}
void PropertyReader::readDelta(Bitstream& in, QObject* object, const QObject* reference) const {
QVariant value;
_reader.readDelta(in, value, (_property.isValid() && reference) ? _property.read(reference) : QVariant());
if (_property.isValid() && object) {
_property.write(object, value);
}
}
MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) :
_name(name),
_streamer(streamer) {
}
TypeStreamer::~TypeStreamer() {
}
const QVector<MetaField>& TypeStreamer::getMetaFields() const {
static QVector<MetaField> emptyMetaFields;
return emptyMetaFields;
}
int TypeStreamer::getFieldIndex(const QByteArray& name) const {
return -1;
}
void TypeStreamer::setField(QVariant& object, int index, const QVariant& value) const {
// nothing by default
}
QVariant TypeStreamer::getField(const QVariant& object, int index) const {
return QVariant();
}
TypeReader::Type TypeStreamer::getReaderType() const {
return TypeReader::SIMPLE_TYPE;
}
const TypeStreamer* TypeStreamer::getKeyStreamer() const {
return NULL;
}
const TypeStreamer* TypeStreamer::getValueStreamer() const {
return NULL;
}
void TypeStreamer::insert(QVariant& object, const QVariant& element) const {
// nothing by default
}
void TypeStreamer::insert(QVariant& object, const QVariant& key, const QVariant& value) const {
// nothing by default
}
bool TypeStreamer::remove(QVariant& object, const QVariant& key) const {
return false;
}
QVariant TypeStreamer::getValue(const QVariant& object, const QVariant& key) const {
return QVariant();
}
void TypeStreamer::prune(QVariant& object, int size) const {
// nothing by default
}
QVariant TypeStreamer::getValue(const QVariant& object, int index) const {
return QVariant();
}
void TypeStreamer::setValue(QVariant& object, int index, const QVariant& value) const {
// nothing by default
}

View file

@ -10,11 +10,13 @@
#define __interface__Bitstream__
#include <QHash>
#include <QMetaProperty>
#include <QMetaType>
#include <QPointer>
#include <QScriptString>
#include <QSharedPointer>
#include <QVariant>
#include <QVector>
#include <QtDebug>
#include <glm/glm.hpp>
@ -29,7 +31,11 @@ class QUrl;
class Attribute;
class AttributeValue;
class Bitstream;
class FieldReader;
class ObjectReader;
class OwnedAttributeValue;
class PropertyReader;
class TypeReader;
class TypeStreamer;
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
@ -55,26 +61,30 @@ private:
/// Provides a means to stream repeated values efficiently. The value is first streamed along with a unique ID. When
/// subsequently streamed, only the ID is sent.
template<class T, class P = T> class RepeatedValueStreamer {
template<class K, class P = K, class V = K> class RepeatedValueStreamer {
public:
RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream),
_lastPersistentID(0), _lastTransientOffset(0) { }
QHash<T, int> getAndResetTransientOffsets();
QHash<K, int> getAndResetTransientOffsets();
void persistTransientOffsets(const QHash<T, int>& transientOffsets);
void persistTransientOffsets(const QHash<K, int>& transientOffsets);
QHash<int, T> getAndResetTransientValues();
QHash<int, V> getAndResetTransientValues();
void persistTransientValues(const QHash<int, T>& transientValues);
void persistTransientValues(const QHash<int, V>& transientValues);
void removePersistentID(P value) { _persistentIDs.remove(value); }
int takePersistentID(P value) { return _persistentIDs.take(value); }
T takePersistentValue(int id) { T value = _persistentValues.take(id); _persistentIDs.remove(value); return value; }
void removePersistentValue(V value) { int id = _valueIDs.take(value); _persistentValues.remove(id); }
RepeatedValueStreamer& operator<<(T value);
RepeatedValueStreamer& operator>>(T& value);
V takePersistentValue(int id) { V value = _persistentValues.take(id); _valueIDs.remove(value); return value; }
RepeatedValueStreamer& operator<<(K value);
RepeatedValueStreamer& operator>>(V& value);
private:
@ -83,23 +93,24 @@ private:
int _lastPersistentID;
int _lastTransientOffset;
QHash<P, int> _persistentIDs;
QHash<T, int> _transientOffsets;
QHash<int, T> _persistentValues;
QHash<int, T> _transientValues;
QHash<K, int> _transientOffsets;
QHash<int, V> _persistentValues;
QHash<int, V> _transientValues;
QHash<V, int> _valueIDs;
};
template<class T, class P> inline QHash<T, int> RepeatedValueStreamer<T, P>::getAndResetTransientOffsets() {
QHash<T, int> transientOffsets;
template<class K, class P, class V> inline QHash<K, int> RepeatedValueStreamer<K, P, V>::getAndResetTransientOffsets() {
QHash<K, int> transientOffsets;
_transientOffsets.swap(transientOffsets);
_lastTransientOffset = 0;
_idStreamer.setBitsFromValue(_lastPersistentID);
return transientOffsets;
}
template<class T, class P> inline void RepeatedValueStreamer<T, P>::persistTransientOffsets(
const QHash<T, int>& transientOffsets) {
template<class K, class P, class V> inline void RepeatedValueStreamer<K, P, V>::persistTransientOffsets(
const QHash<K, int>& transientOffsets) {
int oldLastPersistentID = _lastPersistentID;
for (typename QHash<T, int>::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) {
for (typename QHash<K, int>::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) {
int& id = _persistentIDs[it.key()];
if (id == 0) {
id = oldLastPersistentID + it.value();
@ -109,18 +120,18 @@ template<class T, class P> inline void RepeatedValueStreamer<T, P>::persistTrans
_idStreamer.setBitsFromValue(_lastPersistentID);
}
template<class T, class P> inline QHash<int, T> RepeatedValueStreamer<T, P>::getAndResetTransientValues() {
QHash<int, T> transientValues;
template<class K, class P, class V> inline QHash<int, V> RepeatedValueStreamer<K, P, V>::getAndResetTransientValues() {
QHash<int, V> transientValues;
_transientValues.swap(transientValues);
_idStreamer.setBitsFromValue(_lastPersistentID);
return transientValues;
}
template<class T, class P> inline void RepeatedValueStreamer<T, P>::persistTransientValues(
const QHash<int, T>& transientValues) {
template<class K, class P, class V> inline void RepeatedValueStreamer<K, P, V>::persistTransientValues(
const QHash<int, V>& transientValues) {
int oldLastPersistentID = _lastPersistentID;
for (typename QHash<int, T>::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) {
int& id = _persistentIDs[it.value()];
for (typename QHash<int, V>::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) {
int& id = _valueIDs[it.value()];
if (id == 0) {
id = oldLastPersistentID + it.key();
_lastPersistentID = qMax(_lastPersistentID, id);
@ -130,7 +141,8 @@ template<class T, class P> inline void RepeatedValueStreamer<T, P>::persistTrans
_idStreamer.setBitsFromValue(_lastPersistentID);
}
template<class T, class P> inline RepeatedValueStreamer<T, P>& RepeatedValueStreamer<T, P>::operator<<(T value) {
template<class K, class P, class V> inline RepeatedValueStreamer<K, P, V>&
RepeatedValueStreamer<K, P, V>::operator<<(K value) {
int id = _persistentIDs.value(value);
if (id == 0) {
int& offset = _transientOffsets[value];
@ -147,7 +159,8 @@ template<class T, class P> inline RepeatedValueStreamer<T, P>& RepeatedValueStre
return *this;
}
template<class T, class P> inline RepeatedValueStreamer<T, P>& RepeatedValueStreamer<T, P>::operator>>(T& value) {
template<class K, class P, class V> inline RepeatedValueStreamer<K, P, V>&
RepeatedValueStreamer<K, P, V>::operator>>(V& value) {
int id;
_idStreamer >> id;
if (id <= _lastPersistentID) {
@ -155,7 +168,7 @@ template<class T, class P> inline RepeatedValueStreamer<T, P>& RepeatedValueStre
} else {
int offset = id - _lastPersistentID;
typename QHash<int, T>::iterator it = _transientValues.find(offset);
typename QHash<int, V>::iterator it = _transientValues.find(offset);
if (it == _transientValues.end()) {
_stream > value;
_transientValues.insert(offset, value);
@ -184,8 +197,8 @@ public:
class ReadMappings {
public:
QHash<int, const QMetaObject*> metaObjectValues;
QHash<int, const TypeStreamer*> typeStreamerValues;
QHash<int, ObjectReader> metaObjectValues;
QHash<int, TypeReader> typeStreamerValues;
QHash<int, AttributePointer> attributeValues;
QHash<int, QScriptString> scriptStringValues;
QHash<int, SharedObjectPointer> sharedObjectValues;
@ -199,14 +212,25 @@ public:
/// \return zero; the function only returns a value so that it can be used in static initialization
static int registerTypeStreamer(int type, TypeStreamer* streamer);
/// Returns the streamer registered for the supplied type, if any.
static const TypeStreamer* getTypeStreamer(int type);
/// Returns the meta-object registered under the supplied class name, if any.
static const QMetaObject* getMetaObject(const QByteArray& className);
/// Returns the list of registered subclasses for the supplied meta-object.
static QList<const QMetaObject*> getMetaObjectSubClasses(const QMetaObject* metaObject);
enum MetadataType { NO_METADATA, HASH_METADATA, FULL_METADATA };
/// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both.
Bitstream(QDataStream& underlying, QObject* parent = NULL);
Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA, QObject* parent = NULL);
/// Substitutes the supplied metaobject for the given class name's default mapping.
void addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject);
/// Substitutes the supplied type for the given type name's default mapping.
void addTypeSubstitution(const QByteArray& typeName, int type);
/// Writes a set of bits to the underlying stream.
/// \param bits the number of bits to write
@ -248,6 +272,31 @@ public:
/// Removes a shared object from the read mappings.
void clearSharedObject(int id);
void writeDelta(bool value, bool reference);
void readDelta(bool& value, bool reference);
void writeDelta(const QVariant& value, const QVariant& reference);
template<class T> void writeDelta(const T& value, const T& reference);
template<class T> void readDelta(T& value, const T& reference);
void readRawDelta(QVariant& value, const QVariant& reference);
void writeRawDelta(const QObject* value, const QObject* reference);
void readRawDelta(QObject*& value, const QObject* reference);
template<class T> void writeRawDelta(const T& value, const T& reference);
template<class T> void readRawDelta(T& value, const T& reference);
template<class T> void writeRawDelta(const QList<T>& value, const QList<T>& reference);
template<class T> void readRawDelta(QList<T>& value, const QList<T>& reference);
template<class T> void writeRawDelta(const QSet<T>& value, const QSet<T>& reference);
template<class T> void readRawDelta(QSet<T>& value, const QSet<T>& reference);
template<class K, class V> void writeRawDelta(const QHash<K, V>& value, const QHash<K, V>& reference);
template<class K, class V> void readRawDelta(QHash<K, V>& value, const QHash<K, V>& reference);
Bitstream& operator<<(bool value);
Bitstream& operator>>(bool& value);
@ -295,9 +344,11 @@ public:
Bitstream& operator<<(const QMetaObject* metaObject);
Bitstream& operator>>(const QMetaObject*& metaObject);
Bitstream& operator>>(ObjectReader& objectReader);
Bitstream& operator<<(const TypeStreamer* streamer);
Bitstream& operator>>(const TypeStreamer*& streamer);
Bitstream& operator>>(TypeReader& reader);
Bitstream& operator<<(const AttributePointer& attribute);
Bitstream& operator>>(AttributePointer& attribute);
@ -309,10 +360,10 @@ public:
Bitstream& operator>>(SharedObjectPointer& object);
Bitstream& operator<(const QMetaObject* metaObject);
Bitstream& operator>(const QMetaObject*& metaObject);
Bitstream& operator>(ObjectReader& objectReader);
Bitstream& operator<(const TypeStreamer* streamer);
Bitstream& operator>(const TypeStreamer*& streamer);
Bitstream& operator>(TypeReader& reader);
Bitstream& operator<(const AttributePointer& attribute);
Bitstream& operator>(AttributePointer& attribute);
@ -333,25 +384,195 @@ private slots:
private:
void readProperties(QObject* object);
QDataStream& _underlying;
quint8 _byte;
int _position;
RepeatedValueStreamer<const QMetaObject*> _metaObjectStreamer;
RepeatedValueStreamer<const TypeStreamer*> _typeStreamerStreamer;
MetadataType _metadataType;
RepeatedValueStreamer<const QMetaObject*, const QMetaObject*, ObjectReader> _metaObjectStreamer;
RepeatedValueStreamer<const TypeStreamer*, const TypeStreamer*, TypeReader> _typeStreamerStreamer;
RepeatedValueStreamer<AttributePointer> _attributeStreamer;
RepeatedValueStreamer<QScriptString> _scriptStringStreamer;
RepeatedValueStreamer<SharedObjectPointer, SharedObject*> _sharedObjectStreamer;
WeakSharedObjectHash _sharedObjectReferences;
WeakSharedObjectHash _weakSharedObjectHash;
QHash<QByteArray, const QMetaObject*> _metaObjectSubstitutions;
QHash<QByteArray, const TypeStreamer*> _typeStreamerSubstitutions;
static QHash<QByteArray, const QMetaObject*>& getMetaObjects();
static QMultiHash<const QMetaObject*, const QMetaObject*>& getMetaObjectSubClasses();
static QHash<int, const TypeStreamer*>& getTypeStreamers();
static QVector<PropertyReader> getPropertyReaders(const QMetaObject* metaObject);
};
template<class T> inline void Bitstream::writeDelta(const T& value, const T& reference) {
if (value == reference) {
*this << false;
} else {
*this << true;
writeRawDelta(value, reference);
}
}
template<class T> inline void Bitstream::readDelta(T& value, const T& reference) {
bool changed;
*this >> changed;
if (changed) {
readRawDelta(value, reference);
} else {
value = reference;
}
}
template<class T> inline void Bitstream::writeRawDelta(const T& value, const T& reference) {
*this << value;
}
template<class T> inline void Bitstream::readRawDelta(T& value, const T& reference) {
*this >> value;
}
template<class T> inline void Bitstream::writeRawDelta(const QList<T>& value, const QList<T>& reference) {
*this << value.size();
*this << reference.size();
for (int i = 0; i < value.size(); i++) {
if (i < reference.size()) {
writeDelta(value.at(i), reference.at(i));
} else {
*this << value.at(i);
}
}
}
template<class T> inline void Bitstream::readRawDelta(QList<T>& value, const QList<T>& reference) {
value = reference;
int size, referenceSize;
*this >> size >> referenceSize;
if (size < value.size()) {
value.erase(value.begin() + size, value.end());
}
for (int i = 0; i < size; i++) {
if (i < referenceSize) {
readDelta(value[i], reference.at(i));
} else {
T element;
*this >> element;
value.append(element);
}
}
}
template<class T> inline void Bitstream::writeRawDelta(const QSet<T>& value, const QSet<T>& reference) {
int addedOrRemoved = 0;
foreach (const T& element, value) {
if (!reference.contains(element)) {
addedOrRemoved++;
}
}
foreach (const T& element, reference) {
if (!value.contains(element)) {
addedOrRemoved++;
}
}
*this << addedOrRemoved;
foreach (const T& element, value) {
if (!reference.contains(element)) {
*this << element;
}
}
foreach (const T& element, reference) {
if (!value.contains(element)) {
*this << element;
}
}
}
template<class T> inline void Bitstream::readRawDelta(QSet<T>& value, const QSet<T>& reference) {
value = reference;
int addedOrRemoved;
*this >> addedOrRemoved;
for (int i = 0; i < addedOrRemoved; i++) {
T element;
*this >> element;
if (!value.remove(element)) {
value.insert(element);
}
}
}
template<class K, class V> inline void Bitstream::writeRawDelta(const QHash<K, V>& value, const QHash<K, V>& reference) {
int added = 0;
int modified = 0;
for (typename QHash<K, V>::const_iterator it = value.constBegin(); it != value.constEnd(); it++) {
typename QHash<K, V>::const_iterator previous = reference.find(it.key());
if (previous == reference.constEnd()) {
added++;
} else if (previous.value() != it.value()) {
modified++;
}
}
*this << added;
for (typename QHash<K, V>::const_iterator it = value.constBegin(); it != value.constEnd(); it++) {
if (!reference.contains(it.key())) {
*this << it.key();
*this << it.value();
}
}
*this << modified;
for (typename QHash<K, V>::const_iterator it = value.constBegin(); it != value.constEnd(); it++) {
typename QHash<K, V>::const_iterator previous = reference.find(it.key());
if (previous != reference.constEnd() && previous.value() != it.value()) {
*this << it.key();
writeDelta(it.value(), previous.value());
}
}
int removed = 0;
for (typename QHash<K, V>::const_iterator it = reference.constBegin(); it != reference.constEnd(); it++) {
if (!value.contains(it.key())) {
removed++;
}
}
*this << removed;
for (typename QHash<K, V>::const_iterator it = reference.constBegin(); it != reference.constEnd(); it++) {
if (!value.contains(it.key())) {
*this << it.key();
}
}
}
template<class K, class V> inline void Bitstream::readRawDelta(QHash<K, V>& value, const QHash<K, V>& reference) {
value = reference;
int added;
*this >> added;
for (int i = 0; i < added; i++) {
K key;
V mapping;
*this >> key >> mapping;
value.insert(key, mapping);
}
int modified;
*this >> modified;
for (int i = 0; i < modified; i++) {
K key;
*this >> key;
V& mapping = value[key];
V newMapping;
readDelta(newMapping, mapping);
mapping = newMapping;
}
int removed;
*this >> removed;
for (int i = 0; i < removed; i++) {
K key;
*this >> key;
value.remove(key);
}
}
template<class T> inline Bitstream& Bitstream::operator<<(const QList<T>& list) {
*this << list.size();
foreach (const T& entry, list) {
@ -418,6 +639,119 @@ template<class K, class V> inline Bitstream& Bitstream::operator>>(QHash<K, V>&
return *this;
}
typedef QSharedPointer<TypeReader> TypeReaderPointer;
/// Contains the information required to read a type from the stream.
class TypeReader {
public:
enum Type { SIMPLE_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE };
TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL, bool exactMatch = true,
Type type = SIMPLE_TYPE, const TypeReaderPointer& keyReader = TypeReaderPointer(),
const TypeReaderPointer& valueReader = TypeReaderPointer(),
const QVector<FieldReader>& fields = QVector<FieldReader>());
const QByteArray& getTypeName() const { return _typeName; }
const TypeStreamer* getStreamer() const { return _streamer; }
QVariant read(Bitstream& in) const;
void readDelta(Bitstream& in, QVariant& object, const QVariant& reference) const;
void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const;
bool matchesExactly(const TypeStreamer* streamer) const;
bool operator==(const TypeReader& other) const { return _typeName == other._typeName; }
bool operator!=(const TypeReader& other) const { return _typeName != other._typeName; }
private:
QByteArray _typeName;
const TypeStreamer* _streamer;
bool _exactMatch;
Type _type;
TypeReaderPointer _keyReader;
TypeReaderPointer _valueReader;
QVector<FieldReader> _fields;
};
uint qHash(const TypeReader& typeReader, uint seed = 0);
/// Contains the information required to read a metatype field from the stream and apply it.
class FieldReader {
public:
FieldReader(const TypeReader& reader = TypeReader(), int index = -1);
const TypeReader& getReader() const { return _reader; }
int getIndex() const { return _index; }
void read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const;
void readDelta(Bitstream& in, const TypeStreamer* streamer, QVariant& object, const QVariant& reference) const;
private:
TypeReader _reader;
int _index;
};
/// Contains the information required to read an object from the stream.
class ObjectReader {
public:
ObjectReader(const QByteArray& className = QByteArray(), const QMetaObject* metaObject = NULL,
const QVector<PropertyReader>& properties = QVector<PropertyReader>());
const QByteArray& getClassName() const { return _className; }
const QMetaObject* getMetaObject() const { return _metaObject; }
QObject* read(Bitstream& in, QObject* object = NULL) const;
QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const;
bool operator==(const ObjectReader& other) const { return _className == other._className; }
bool operator!=(const ObjectReader& other) const { return _className != other._className; }
private:
QByteArray _className;
const QMetaObject* _metaObject;
QVector<PropertyReader> _properties;
};
uint qHash(const ObjectReader& objectReader, uint seed = 0);
/// Contains the information required to read an object property from the stream and apply it.
class PropertyReader {
public:
PropertyReader(const TypeReader& reader = TypeReader(), const QMetaProperty& property = QMetaProperty());
const TypeReader& getReader() const { return _reader; }
void read(Bitstream& in, QObject* object) const;
void readDelta(Bitstream& in, QObject* object, const QObject* reference) const;
private:
TypeReader _reader;
QMetaProperty _property;
};
/// Describes a metatype field.
class MetaField {
public:
MetaField(const QByteArray& name = QByteArray(), const TypeStreamer* streamer = NULL);
const QByteArray& getName() const { return _name; }
const TypeStreamer* getStreamer() const { return _streamer; }
private:
QByteArray _name;
const TypeStreamer* _streamer;
};
Q_DECLARE_METATYPE(const QMetaObject*)
/// Macro for registering streamable meta-objects.
@ -427,12 +761,42 @@ Q_DECLARE_METATYPE(const QMetaObject*)
class TypeStreamer {
public:
virtual ~TypeStreamer();
void setType(int type) { _type = type; }
int getType() const { return _type; }
virtual bool equal(const QVariant& first, const QVariant& second) const = 0;
virtual void write(Bitstream& out, const QVariant& value) const = 0;
virtual QVariant read(Bitstream& in) const = 0;
virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0;
virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0;
virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0;
virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0;
virtual const QVector<MetaField>& getMetaFields() const;
virtual int getFieldIndex(const QByteArray& name) const;
virtual void setField(QVariant& object, int index, const QVariant& value) const;
virtual QVariant getField(const QVariant& object, int index) const;
virtual TypeReader::Type getReaderType() const;
virtual const TypeStreamer* getKeyStreamer() const;
virtual const TypeStreamer* getValueStreamer() const;
virtual void insert(QVariant& object, const QVariant& value) const;
virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const;
virtual bool remove(QVariant& object, const QVariant& key) const;
virtual QVariant getValue(const QVariant& object, const QVariant& key) const;
virtual void prune(QVariant& object, int size) const;
virtual QVariant getValue(const QVariant& object, int index) const;
virtual void setValue(QVariant& object, int index, const QVariant& value) const;
private:
int _type;
@ -442,25 +806,95 @@ private:
template<class T> class SimpleTypeStreamer : public TypeStreamer {
public:
virtual bool equal(const QVariant& first, const QVariant& second) const { return first.value<T>() == second.value<T>(); }
virtual void write(Bitstream& out, const QVariant& value) const { out << value.value<T>(); }
virtual QVariant read(Bitstream& in) const { T value; in >> value; return QVariant::fromValue(value); }
virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const {
out.writeDelta(value.value<T>(), reference.value<T>()); }
virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const {
in.readDelta(*static_cast<T*>(value.data()), reference.value<T>()); }
virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const {
out.writeRawDelta(value.value<T>(), reference.value<T>()); }
virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const {
in.readRawDelta(*static_cast<T*>(value.data()), reference.value<T>()); }
};
/// A streamer for types compiled by mtc.
template<class T> class StreamableTypeStreamer : public SimpleTypeStreamer<T> {
public:
virtual TypeReader::Type getReaderType() const { return TypeReader::STREAMABLE_TYPE; }
virtual const QVector<MetaField>& getMetaFields() const { return T::getMetaFields(); }
virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); }
virtual void setField(QVariant& object, int index, const QVariant& value) const {
static_cast<T*>(object.data())->setField(index, value); }
virtual QVariant getField(const QVariant& object, int index) const {
return static_cast<const T*>(object.constData())->getField(index); }
};
/// Base template for collection streamers.
template<class T> class CollectionTypeStreamer : public SimpleTypeStreamer<T> {
};
/// A streamer for list types.
template<class T> class CollectionTypeStreamer<QList<T> > : public SimpleTypeStreamer<QList<T> > {
public:
virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; }
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); }
virtual void insert(QVariant& object, const QVariant& value) const {
static_cast<QList<T>*>(object.data())->append(value.value<T>()); }
virtual void prune(QVariant& object, int size) const {
QList<T>* list = static_cast<QList<T>*>(object.data()); list->erase(list->begin() + size, list->end()); }
virtual QVariant getValue(const QVariant& object, int index) const {
return QVariant::fromValue(static_cast<const QList<T>*>(object.constData())->at(index)); }
virtual void setValue(QVariant& object, int index, const QVariant& value) const {
static_cast<QList<T>*>(object.data())->replace(index, value.value<T>()); }
};
/// A streamer for set types.
template<class T> class CollectionTypeStreamer<QSet<T> > : public SimpleTypeStreamer<QSet<T> > {
public:
virtual TypeReader::Type getReaderType() const { return TypeReader::SET_TYPE; }
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<T>()); }
virtual void insert(QVariant& object, const QVariant& value) const {
static_cast<QSet<T>*>(object.data())->insert(value.value<T>()); }
virtual bool remove(QVariant& object, const QVariant& key) const {
return static_cast<QSet<T>*>(object.data())->remove(key.value<T>()); }
};
/// A streamer for hash types.
template<class K, class V> class CollectionTypeStreamer<QHash<K, V> > : public SimpleTypeStreamer<QHash<K, V> > {
public:
virtual TypeReader::Type getReaderType() const { return TypeReader::MAP_TYPE; }
virtual const TypeStreamer* getKeyStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<K>()); }
virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId<V>()); }
virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const {
static_cast<QHash<K, V>*>(object.data())->insert(key.value<K>(), value.value<V>()); }
virtual bool remove(QVariant& object, const QVariant& key) const {
return static_cast<QHash<K, V>*>(object.data())->remove(key.value<K>()); }
virtual QVariant getValue(const QVariant& object, const QVariant& key) const {
return QVariant::fromValue(static_cast<const QHash<K, V>*>(object.constData())->value(key.value<K>())); }
};
/// Macro for registering simple type streamers.
#define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \
Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer<x>());
Bitstream::registerTypeStreamer(qMetaTypeId<x>(), new SimpleTypeStreamer<x>());
#ifdef WIN32
#define _Pragma __pragma
#endif
/// Macro for registering collection type streamers.
#define REGISTER_COLLECTION_TYPE_STREAMER(x) static int x##Streamer = \
Bitstream::registerTypeStreamer(qMetaTypeId<x>(), new CollectionTypeStreamer<x>());
/// Declares the metatype and the streaming operators. The last lines
/// ensure that the generated file will be included in the link phase.
#define STRINGIFY(x) #x
#ifdef _WIN32
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
Bitstream& operator<<(Bitstream& out, const X& obj); \
Bitstream& operator>>(Bitstream& in, X& obj); \
template<> void Bitstream::writeRawDelta(const X& value, const X& reference); \
template<> void Bitstream::readRawDelta(X& value, const X& reference); \
bool operator==(const X& first, const X& second); \
bool operator!=(const X& first, const X& second); \
static const int* _TypePtr##X = &X::Type;
@ -468,6 +902,8 @@ public:
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
Bitstream& operator<<(Bitstream& out, const X& obj); \
Bitstream& operator>>(Bitstream& in, X& obj); \
template<> void Bitstream::writeRawDelta(const X& value, const X& reference); \
template<> void Bitstream::readRawDelta(X& value, const X& reference); \
bool operator==(const X& first, const X& second); \
bool operator!=(const X& first, const X& second); \
__attribute__((unused)) static const int* _TypePtr##X = &X::Type;
@ -476,6 +912,8 @@ public:
#define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \
Bitstream& operator<<(Bitstream& out, const X& obj); \
Bitstream& operator>>(Bitstream& in, X& obj); \
template<> void Bitstream::writeRawDelta(const X& value, const X& reference); \
template<> void Bitstream::readRawDelta(X& value, const X& reference); \
bool operator==(const X& first, const X& second); \
bool operator!=(const X& first, const X& second); \
static const int* _TypePtr##X = &X::Type; \
@ -485,12 +923,19 @@ public:
/// Registers a streamable type and its streamer.
template<class T> int registerStreamableMetaType() {
int type = qRegisterMetaType<T>();
Bitstream::registerTypeStreamer(type, new SimpleTypeStreamer<T>());
Bitstream::registerTypeStreamer(type, new StreamableTypeStreamer<T>());
return type;
}
/// Flags a class as streamable (use as you would Q_OBJECT).
#define STREAMABLE public: static const int Type; private:
#define STREAMABLE public: \
static const int Type; \
static const QVector<MetaField>& getMetaFields(); \
static int getFieldIndex(const QByteArray& name); \
void setField(int index, const QVariant& value); \
QVariant getField(int index) const; \
private: \
static QHash<QByteArray, int> createFieldIndices();
/// Flags a field or base class as streaming.
#define STREAM

View file

@ -420,7 +420,7 @@ bool CircularBuffer::atEnd() const {
}
qint64 CircularBuffer::bytesAvailable() const {
return _size - _offset + QIODevice::bytesAvailable();
return _size - _offset;
}
bool CircularBuffer::canReadLine() const {

View file

@ -85,7 +85,7 @@ void MetavoxelData::guide(MetavoxelVisitor& visitor) {
const QVector<AttributePointer>& inputs = visitor.getInputs();
const QVector<AttributePointer>& outputs = visitor.getOutputs();
MetavoxelVisitation firstVisitation = { NULL, visitor, QVector<MetavoxelNode*>(inputs.size() + 1),
QVector<MetavoxelNode*>(outputs.size()), { getMinimum(), _size,
QVector<MetavoxelNode*>(outputs.size()), { NULL, getMinimum(), _size,
QVector<AttributeValue>(inputs.size() + 1), QVector<OwnedAttributeValue>(outputs.size()) } };
for (int i = 0; i < inputs.size(); i++) {
MetavoxelNode* node = _roots.value(inputs.at(i));
@ -177,7 +177,7 @@ template<SpannerUpdateFunction F> int SpannerUpdateVisitor<F>::visit(MetavoxelIn
void MetavoxelData::insert(const AttributePointer& attribute, const SharedObjectPointer& object) {
Spanner* spanner = static_cast<Spanner*>(object.data());
insert(attribute, spanner->getBounds(), spanner->getGranularity(), object);
insert(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), object);
}
void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds,
@ -192,7 +192,7 @@ void MetavoxelData::insert(const AttributePointer& attribute, const Box& bounds,
void MetavoxelData::remove(const AttributePointer& attribute, const SharedObjectPointer& object) {
Spanner* spanner = static_cast<Spanner*>(object.data());
remove(attribute, spanner->getBounds(), spanner->getGranularity(), object);
remove(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), object);
}
void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds,
@ -203,7 +203,7 @@ void MetavoxelData::remove(const AttributePointer& attribute, const Box& bounds,
void MetavoxelData::toggle(const AttributePointer& attribute, const SharedObjectPointer& object) {
Spanner* spanner = static_cast<Spanner*>(object.data());
toggle(attribute, spanner->getBounds(), spanner->getGranularity(), object);
toggle(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), object);
}
void MetavoxelData::toggle(const AttributePointer& attribute, const Box& bounds,
@ -212,6 +212,67 @@ void MetavoxelData::toggle(const AttributePointer& attribute, const Box& bounds,
guide(visitor);
}
void MetavoxelData::replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject,
const SharedObjectPointer& newObject) {
Spanner* spanner = static_cast<Spanner*>(oldObject.data());
replace(attribute, spanner->getBounds(), spanner->getPlacementGranularity(), oldObject, newObject);
}
class SpannerReplaceVisitor : public MetavoxelVisitor {
public:
SpannerReplaceVisitor(const AttributePointer& attribute, const Box& bounds,
float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject);
virtual int visit(MetavoxelInfo& info);
private:
const AttributePointer& _attribute;
const Box& _bounds;
float _longestSide;
const SharedObjectPointer& _oldObject;
const SharedObjectPointer& _newObject;
};
SpannerReplaceVisitor::SpannerReplaceVisitor(const AttributePointer& attribute, const Box& bounds, float granularity,
const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) :
MetavoxelVisitor(QVector<AttributePointer>() << attribute, QVector<AttributePointer>() << attribute),
_attribute(attribute),
_bounds(bounds),
_longestSide(qMax(bounds.getLongestSide(), granularity)),
_oldObject(oldObject),
_newObject(newObject) {
}
int SpannerReplaceVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (info.size > _longestSide) {
return DEFAULT_ORDER;
}
SharedObjectSet set = info.inputValues.at(0).getInlineValue<SharedObjectSet>();
if (set.remove(_oldObject)) {
set.insert(_newObject);
}
info.outputValues[0] = AttributeValue(_attribute, encodeInline(set));
return STOP_RECURSION;
}
void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds, float granularity,
const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject) {
Spanner* newSpanner = static_cast<Spanner*>(newObject.data());
if (bounds != newSpanner->getBounds() || granularity != newSpanner->getPlacementGranularity()) {
// if the bounds have changed, we must remove and reinsert
remove(attribute, bounds, granularity, oldObject);
insert(attribute, newSpanner->getBounds(), newSpanner->getPlacementGranularity(), newObject);
return;
}
SpannerReplaceVisitor visitor(attribute, bounds, granularity, oldObject, newObject);
guide(visitor);
}
void MetavoxelData::clear(const AttributePointer& attribute) {
MetavoxelNode* node = _roots.take(attribute);
if (node) {
@ -239,7 +300,7 @@ private:
FirstRaySpannerIntersectionVisitor::FirstRaySpannerIntersectionVisitor(
const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, const MetavoxelLOD& lod) :
RaySpannerIntersectionVisitor(origin, direction, QVector<AttributePointer>() << attribute,
QVector<AttributePointer>(), QVector<AttributePointer>(), lod),
QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(), lod),
_spanner(NULL) {
}
@ -485,14 +546,26 @@ void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) {
minimum = getNextMinimum(lastMinimum, size, index);
}
MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue) : _referenceCount(1) {
MetavoxelNode::MetavoxelNode(const AttributeValue& attributeValue, const MetavoxelNode* copyChildren) :
_referenceCount(1) {
_attributeValue = attributeValue.copy();
for (int i = 0; i < CHILD_COUNT; i++) {
_children[i] = NULL;
if (copyChildren) {
for (int i = 0; i < CHILD_COUNT; i++) {
if ((_children[i] = copyChildren->_children[i])) {
_children[i]->incrementReferenceCount();
}
}
} else {
for (int i = 0; i < CHILD_COUNT; i++) {
_children[i] = NULL;
}
}
}
MetavoxelNode::MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy) : _referenceCount(1) {
MetavoxelNode::MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy) :
_referenceCount(1) {
_attributeValue = attribute->create(copy->_attributeValue);
for (int i = 0; i < CHILD_COUNT; i++) {
if ((_children[i] = copy->_children[i])) {
@ -511,14 +584,17 @@ AttributeValue MetavoxelNode::getAttributeValue(const AttributePointer& attribut
return AttributeValue(attribute, _attributeValue);
}
void MetavoxelNode::mergeChildren(const AttributePointer& attribute) {
void MetavoxelNode::mergeChildren(const AttributePointer& attribute, bool postRead) {
if (isLeaf()) {
return;
}
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) {
if (attribute->merge(_attributeValue, childValues, postRead) && allLeaves) {
clearChildren(attribute);
}
}
@ -550,7 +626,7 @@ void MetavoxelNode::read(MetavoxelStreamState& state) {
_children[i] = new MetavoxelNode(state.attribute);
_children[i]->read(nextState);
}
mergeChildren(state.attribute);
mergeChildren(state.attribute, true);
}
}
@ -608,7 +684,7 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta
}
}
}
mergeChildren(state.attribute);
mergeChildren(state.attribute, true);
}
}
@ -863,10 +939,11 @@ void MetavoxelVisitor::prepare() {
// nothing by default
}
SpannerVisitor::SpannerVisitor(const QVector<AttributePointer>& spannerInputs, const QVector<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs, const MetavoxelLOD& lod) :
MetavoxelVisitor(inputs + spannerInputs, outputs, lod),
_spannerInputCount(spannerInputs.size()) {
SpannerVisitor::SpannerVisitor(const QVector<AttributePointer>& spannerInputs, const QVector<AttributePointer>& spannerMasks,
const QVector<AttributePointer>& inputs, const QVector<AttributePointer>& outputs, const MetavoxelLOD& lod) :
MetavoxelVisitor(inputs + spannerInputs + spannerMasks, outputs, lod),
_spannerInputCount(spannerInputs.size()),
_spannerMaskCount(spannerMasks.size()) {
}
void SpannerVisitor::prepare() {
@ -874,17 +951,34 @@ void SpannerVisitor::prepare() {
}
int SpannerVisitor::visit(MetavoxelInfo& info) {
for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) {
for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) {
foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue<SharedObjectSet>()) {
Spanner* spanner = static_cast<Spanner*>(object.data());
if (spanner->testAndSetVisited()) {
if (!visit(spanner)) {
return SHORT_CIRCUIT;
}
if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited() &&
!visit(spanner, glm::vec3(), 0.0f)) {
return SHORT_CIRCUIT;
}
}
}
return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER;
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) {
float maskValue = info.inputValues.at(i).getInlineValue<float>();
if (maskValue < 0.5f) {
const MetavoxelInfo* nextInfo = &info;
do {
foreach (const SharedObjectPointer& object, nextInfo->inputValues.at(
i - _spannerInputCount).getInlineValue<SharedObjectSet>()) {
Spanner* spanner = static_cast<Spanner*>(object.data());
if (spanner->isMasked() && !visit(spanner, info.minimum, info.size)) {
return SHORT_CIRCUIT;
}
}
} while ((nextInfo = nextInfo->parentInfo));
}
}
return STOP_RECURSION;
}
RayIntersectionVisitor::RayIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction,
@ -904,10 +998,11 @@ int RayIntersectionVisitor::visit(MetavoxelInfo& info) {
}
RaySpannerIntersectionVisitor::RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction,
const QVector<AttributePointer>& spannerInputs, const QVector<AttributePointer>& inputs,
const QVector<AttributePointer>& outputs, const MetavoxelLOD& lod) :
RayIntersectionVisitor(origin, direction, inputs + spannerInputs, outputs, lod),
_spannerInputCount(spannerInputs.size()) {
const QVector<AttributePointer>& spannerInputs, const QVector<AttributePointer>& spannerMasks,
const QVector<AttributePointer>& inputs, const QVector<AttributePointer>& outputs, const MetavoxelLOD& lod) :
RayIntersectionVisitor(origin, direction, inputs + spannerInputs + spannerMasks, outputs, lod),
_spannerInputCount(spannerInputs.size()),
_spannerMaskCount(spannerMasks.size()) {
}
void RaySpannerIntersectionVisitor::prepare() {
@ -926,12 +1021,12 @@ bool operator<(const SpannerDistance& first, const SpannerDistance& second) {
int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
QVarLengthArray<SpannerDistance, 4> spannerDistances;
for (int i = _inputs.size() - _spannerInputCount; i < _inputs.size(); i++) {
for (int end = _inputs.size() - _spannerMaskCount, i = end - _spannerInputCount, j = end; i < end; i++, j++) {
foreach (const SharedObjectPointer& object, info.inputValues.at(i).getInlineValue<SharedObjectSet>()) {
Spanner* spanner = static_cast<Spanner*>(object.data());
if (spanner->testAndSetVisited()) {
if (!(spanner->isMasked() && j < _inputs.size()) && spanner->testAndSetVisited()) {
SpannerDistance spannerDistance = { spanner };
if (spanner->findRayIntersection(_origin, _direction, spannerDistance.distance)) {
if (spanner->findRayIntersection(_origin, _direction, glm::vec3(), 0.0f, spannerDistance.distance)) {
spannerDistances.append(spannerDistance);
}
}
@ -943,7 +1038,36 @@ int RaySpannerIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
}
}
}
return info.isLeaf ? STOP_RECURSION : _order;
if (!info.isLeaf) {
return _order;
}
for (int i = _inputs.size() - _spannerMaskCount; i < _inputs.size(); i++) {
float maskValue = info.inputValues.at(i).getInlineValue<float>();
if (maskValue < 0.5f) {
const MetavoxelInfo* nextInfo = &info;
do {
foreach (const SharedObjectPointer& object, nextInfo->inputValues.at(
i - _spannerInputCount).getInlineValue<SharedObjectSet>()) {
Spanner* spanner = static_cast<Spanner*>(object.data());
if (spanner->isMasked()) {
SpannerDistance spannerDistance = { spanner };
if (spanner->findRayIntersection(_origin, _direction,
info.minimum, info.size, spannerDistance.distance)) {
spannerDistances.append(spannerDistance);
}
}
}
} while ((nextInfo = nextInfo->parentInfo));
qStableSort(spannerDistances);
foreach (const SpannerDistance& spannerDistance, spannerDistances) {
if (!visitSpanner(spannerDistance.spanner, spannerDistance.distance)) {
return SHORT_CIRCUIT;
}
}
}
}
return STOP_RECURSION;
}
DefaultMetavoxelGuide::DefaultMetavoxelGuide() {
@ -953,8 +1077,8 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
float lodBase = glm::distance(visitation.visitor.getLOD().position, visitation.info.getCenter()) *
visitation.visitor.getLOD().threshold;
visitation.info.isLeaf = (visitation.info.size < lodBase * visitation.visitor.getMinimumLODThresholdMultiplier()) ||
visitation.allInputNodesLeaves();
visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor.getMinimumLODThresholdMultiplier());
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
int encodedOrder = visitation.visitor.visit(visitation.info);
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
return false;
@ -969,7 +1093,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
// "set" to same value; disregard
value = AttributeValue();
} else {
node = new MetavoxelNode(value);
node = value.getAttribute()->createMetavoxelNode(value, node);
}
}
if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) {
@ -977,7 +1101,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
}
MetavoxelVisitation nextVisitation = { &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()),
{ &visitation.info, glm::vec3(), visitation.info.size * 0.5f, QVector<AttributeValue>(visitation.inputNodes.size()),
QVector<OwnedAttributeValue>(visitation.outputNodes.size()) } };
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
// the encoded order tells us the child indices for each iteration
@ -991,7 +1115,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
MetavoxelNode* child = (node && (visitation.info.size >= lodBase *
parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ?
child->getAttributeValue(parentValue.getAttribute()) : parentValue;
child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue);
}
for (int j = 0; j < visitation.outputNodes.size(); j++) {
MetavoxelNode* node = visitation.outputNodes.at(j);
@ -1019,7 +1143,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
node = new MetavoxelNode(value.getAttribute(), node);
} else {
// create leaf with inherited value
node = new MetavoxelNode(visitation.getInheritedOutputValue(j));
node = new MetavoxelNode(value.getAttribute()->inherit(visitation.getInheritedOutputValue(j)));
}
}
MetavoxelNode* node = visitation.outputNodes.at(j);
@ -1028,7 +1152,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
child->decrementReferenceCount(value.getAttribute());
} else {
// it's a leaf; we need to split it up
AttributeValue nodeValue = node->getAttributeValue(value.getAttribute());
AttributeValue nodeValue = value.getAttribute()->inherit(node->getAttributeValue(value.getAttribute()));
for (int k = 1; k < MetavoxelNode::CHILD_COUNT; k++) {
node->setChild((index + k) % MetavoxelNode::CHILD_COUNT, new MetavoxelNode(nodeValue));
}
@ -1095,7 +1219,7 @@ QScriptValue ScriptedMetavoxelGuide::visit(QScriptContext* context, QScriptEngin
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()),
NULL, glm::vec3(minimum.property(0).toNumber(), minimum.property(1).toNumber(), minimum.property(2).toNumber()),
(float)infoValue.property(guide->_sizeHandle).toNumber(), guide->_visitation->info.inputValues,
guide->_visitation->info.outputValues, infoValue.property(guide->_isLeafHandle).toBool() };
@ -1202,11 +1326,14 @@ AttributeValue MetavoxelVisitation::getInheritedOutputValue(int index) const {
return AttributeValue(visitor.getOutputs().at(index));
}
const float DEFAULT_GRANULARITY = 0.01f;
const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f;
const float DEFAULT_VOXELIZATION_GRANULARITY = powf(2.0f, -3.0f);
Spanner::Spanner() :
_renderer(NULL),
_granularity(DEFAULT_GRANULARITY),
_placementGranularity(DEFAULT_PLACEMENT_GRANULARITY),
_voxelizationGranularity(DEFAULT_VOXELIZATION_GRANULARITY),
_masked(false),
_lastVisit(0) {
}
@ -1223,7 +1350,16 @@ const QVector<AttributePointer>& Spanner::getAttributes() const {
return emptyVector;
}
bool Spanner::getAttributeValues(MetavoxelInfo& info) const {
const QVector<AttributePointer>& Spanner::getVoxelizedAttributes() const {
static QVector<AttributePointer> emptyVector;
return emptyVector;
}
bool Spanner::getAttributeValues(MetavoxelInfo& info, bool force) const {
return false;
}
bool Spanner::blendAttributeValues(MetavoxelInfo& info, bool force) const {
return false;
}
@ -1250,7 +1386,8 @@ SpannerRenderer* Spanner::getRenderer() {
return _renderer;
}
bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const {
return _bounds.findRayIntersection(origin, direction, distance);
}
@ -1271,11 +1408,12 @@ void SpannerRenderer::simulate(float deltaTime) {
// nothing by default
}
void SpannerRenderer::render(float alpha) {
void SpannerRenderer::render(float alpha, const glm::vec3& clipMinimum, float clipSize) {
// nothing by default
}
bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
bool SpannerRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const {
return false;
}
@ -1320,10 +1458,17 @@ const QVector<AttributePointer>& Sphere::getAttributes() const {
return attributes;
}
bool Sphere::getAttributeValues(MetavoxelInfo& info) const {
const QVector<AttributePointer>& Sphere::getVoxelizedAttributes() const {
static QVector<AttributePointer> attributes = QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getSpannerColorAttribute() <<
AttributeRegistry::getInstance()->getSpannerNormalAttribute();
return attributes;
}
bool Sphere::getAttributeValues(MetavoxelInfo& info, bool force) const {
// bounds check
Box bounds = info.getBounds();
if (!getBounds().intersects(bounds)) {
if (!(force || getBounds().intersects(bounds))) {
return false;
}
// count the points inside the sphere
@ -1336,22 +1481,63 @@ bool Sphere::getAttributeValues(MetavoxelInfo& info) const {
if (pointsWithin == Box::VERTEX_COUNT) {
// entirely contained
info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline<QRgb>(_color.rgba()));
getNormal(info);
info.outputValues[1] = getNormal(info, _color.alpha());
return false;
}
if (info.size <= getGranularity()) {
if (force || info.size <= getVoxelizationGranularity()) {
// best guess
if (pointsWithin > 0) {
int alpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT;
info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline<QRgb>(qRgba(
_color.red(), _color.green(), _color.blue(), _color.alpha() * pointsWithin / Box::VERTEX_COUNT)));
getNormal(info);
_color.red(), _color.green(), _color.blue(), alpha)));
info.outputValues[1] = getNormal(info, alpha);
}
return false;
}
return true;
}
bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
bool Sphere::blendAttributeValues(MetavoxelInfo& info, bool force) const {
// bounds check
Box bounds = info.getBounds();
if (!(force || getBounds().intersects(bounds))) {
return false;
}
// count the points inside the sphere
int pointsWithin = 0;
for (int i = 0; i < Box::VERTEX_COUNT; i++) {
if (glm::distance(bounds.getVertex(i), getTranslation()) <= getScale()) {
pointsWithin++;
}
}
if (pointsWithin == Box::VERTEX_COUNT) {
// entirely contained
info.outputValues[0] = AttributeValue(getAttributes().at(0), encodeInline<QRgb>(_color.rgba()));
info.outputValues[1] = getNormal(info, _color.alpha());
return false;
}
if (force || info.size <= getVoxelizationGranularity()) {
// best guess
if (pointsWithin > 0) {
const AttributeValue& oldColor = info.outputValues.at(0).getAttribute() ?
info.outputValues.at(0) : info.inputValues.at(0);
const AttributeValue& oldNormal = info.outputValues.at(1).getAttribute() ?
info.outputValues.at(1) : info.inputValues.at(1);
int oldAlpha = qAlpha(oldColor.getInlineValue<QRgb>());
int newAlpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT;
float combinedAlpha = (float)newAlpha / (oldAlpha + newAlpha);
int baseAlpha = _color.alpha() * pointsWithin / Box::VERTEX_COUNT;
info.outputValues[0].mix(oldColor, AttributeValue(getAttributes().at(0),
encodeInline<QRgb>(qRgba(_color.red(), _color.green(), _color.blue(), baseAlpha))), combinedAlpha);
info.outputValues[1].mix(oldNormal, getNormal(info, baseAlpha), combinedAlpha);
}
return false;
}
return true;
}
bool Sphere::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const {
return findRaySphereIntersection(origin, direction, getTranslation(), getScale(), distance);
}
@ -1364,22 +1550,21 @@ void Sphere::updateBounds() {
setBounds(Box(getTranslation() - extent, getTranslation() + extent));
}
void Sphere::getNormal(MetavoxelInfo& info) const {
AttributeValue Sphere::getNormal(MetavoxelInfo& info, int alpha) const {
glm::vec3 normal = info.getCenter() - getTranslation();
float length = glm::length(normal);
QRgb color;
if (length > EPSILON) {
if (alpha != 0 && length > EPSILON) {
const float NORMAL_SCALE = 127.0f;
float scale = NORMAL_SCALE / length;
const int BYTE_MASK = 0xFF;
color = qRgb((int)(normal.x * scale) & BYTE_MASK, (int)(normal.y * scale) & BYTE_MASK,
(int)(normal.z * scale) & BYTE_MASK);
color = qRgba((int)(normal.x * scale) & BYTE_MASK, (int)(normal.y * scale) & BYTE_MASK,
(int)(normal.z * scale) & BYTE_MASK, alpha);
} else {
const QRgb DEFAULT_NORMAL = 0x007F00;
color = DEFAULT_NORMAL;
color = QRgb();
}
info.outputValues[1] = AttributeValue(getAttributes().at(1), encodeInline<QRgb>(color));
return AttributeValue(getAttributes().at(1), encodeInline<QRgb>(color));
}
StaticModel::StaticModel() {
@ -1391,10 +1576,11 @@ void StaticModel::setURL(const QUrl& url) {
}
}
bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const {
bool StaticModel::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const {
// delegate to renderer, if we have one
return _renderer ? _renderer->findRayIntersection(origin, direction, distance) :
Spanner::findRayIntersection(origin, direction, distance);
return _renderer ? _renderer->findRayIntersection(origin, direction, clipMinimum, clipSize, distance) :
Spanner::findRayIntersection(origin, direction, clipMinimum, clipSize, distance);
}
QByteArray StaticModel::getRendererClassName() const {

View file

@ -79,6 +79,11 @@ public:
void toggle(const AttributePointer& attribute, const SharedObjectPointer& object);
void toggle(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object);
void replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject,
const SharedObjectPointer& newObject);
void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject,
const SharedObjectPointer& newObject);
void clear(const AttributePointer& attribute);
/// Convenience function that finds the first spanner intersecting the provided ray.
@ -132,7 +137,7 @@ public:
static const int CHILD_COUNT = 8;
MetavoxelNode(const AttributeValue& attributeValue);
MetavoxelNode(const AttributeValue& attributeValue, const MetavoxelNode* copyChildren = NULL);
MetavoxelNode(const AttributePointer& attribute, const MetavoxelNode* copy);
void setAttributeValue(const AttributeValue& attributeValue);
@ -140,7 +145,7 @@ public:
AttributeValue getAttributeValue(const AttributePointer& attribute) const;
void* getAttributeValue() const { return _attributeValue; }
void mergeChildren(const AttributePointer& attribute);
void mergeChildren(const AttributePointer& attribute, bool postRead = false);
MetavoxelNode* getChild(int index) const { return _children[index]; }
void setChild(int index, MetavoxelNode* child) { _children[index] = child; }
@ -185,10 +190,12 @@ private:
class MetavoxelInfo {
public:
MetavoxelInfo* parentInfo;
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> inputValues;
QVector<OwnedAttributeValue> outputValues;
bool isLODLeaf;
bool isLeaf;
Box getBounds() const { return Box(minimum, minimum + glm::vec3(size, size, size)); }
@ -253,13 +260,15 @@ class SpannerVisitor : public MetavoxelVisitor {
public:
SpannerVisitor(const QVector<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& spannerMasks = QVector<AttributePointer>(),
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD());
/// Visits a spanner.
/// Visits a spanner (or part thereof).
/// \param clipSize the size of the clip volume, or zero if unclipped
/// \return true to continue, false to short-circuit the tour
virtual bool visit(Spanner* spanner) = 0;
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) = 0;
virtual void prepare();
virtual int visit(MetavoxelInfo& info);
@ -267,6 +276,7 @@ public:
protected:
int _spannerInputCount;
int _spannerMaskCount;
};
/// Base class for ray intersection visitors.
@ -296,11 +306,12 @@ public:
RaySpannerIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction,
const QVector<AttributePointer>& spannerInputs,
const QVector<AttributePointer>& spannerMasks = QVector<AttributePointer>(),
const QVector<AttributePointer>& inputs = QVector<AttributePointer>(),
const QVector<AttributePointer>& outputs = QVector<AttributePointer>(),
const MetavoxelLOD& lod = MetavoxelLOD());
/// Visits a spanner that the ray intersects.
/// Visits a spannerthat the ray intersects.
/// \return true to continue, false to short-circuit the tour
virtual bool visitSpanner(Spanner* spanner, float distance) = 0;
@ -310,6 +321,7 @@ public:
protected:
int _spannerInputCount;
int _spannerMaskCount;
};
/// Interface for objects that guide metavoxel visitors.
@ -408,8 +420,10 @@ public:
class Spanner : public SharedObject {
Q_OBJECT
Q_PROPERTY(Box bounds MEMBER _bounds WRITE setBounds NOTIFY boundsChanged DESIGNABLE false)
Q_PROPERTY(float granularity MEMBER _granularity DESIGNABLE false)
Q_PROPERTY(float placementGranularity MEMBER _placementGranularity DESIGNABLE false)
Q_PROPERTY(float voxelizationGranularity MEMBER _voxelizationGranularity DESIGNABLE false)
Q_PROPERTY(float masked MEMBER _masked DESIGNABLE false)
public:
/// Increments the value of the global visit counter.
@ -420,15 +434,29 @@ public:
void setBounds(const Box& bounds);
const Box& getBounds() const { return _bounds; }
void setGranularity(float granularity) { _granularity = granularity; }
float getGranularity() const { return _granularity; }
void setPlacementGranularity(float granularity) { _placementGranularity = granularity; }
float getPlacementGranularity() const { return _placementGranularity; }
void setVoxelizationGranularity(float granularity) { _voxelizationGranularity = granularity; }
float getVoxelizationGranularity() const { return _voxelizationGranularity; }
void setMasked(bool masked) { _masked = masked; }
bool isMasked() const { return _masked; }
/// Returns a reference to the list of attributes associated with this spanner.
virtual const QVector<AttributePointer>& getAttributes() const;
/// Returns a reference to the list of corresponding attributes that we voxelize the spanner into.
virtual const QVector<AttributePointer>& getVoxelizedAttributes() const;
/// Sets the attribute values associated with this spanner in the supplied info.
/// \return true to recurse, false to stop
virtual bool getAttributeValues(MetavoxelInfo& info) const;
virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const;
/// Blends the attribute values associated with this spanner into the supplied info.
/// \param force if true, blend even if we would normally subdivide
/// \return true to recurse, false to stop
virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const;
/// Checks whether we've visited this object on the current traversal. If we have, returns false.
/// If we haven't, sets the last visit identifier and returns true.
@ -438,7 +466,9 @@ public:
SpannerRenderer* getRenderer();
/// Finds the intersection between the described ray and this spanner.
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
/// \param clipSize the size of the clip region, or zero if unclipped
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
signals:
@ -455,7 +485,9 @@ protected:
private:
Box _bounds;
float _granularity;
float _placementGranularity;
float _voxelizationGranularity;
bool _masked;
int _lastVisit; ///< the identifier of the last visit
static int _visit; ///< the global visit counter
@ -471,8 +503,9 @@ public:
virtual void init(Spanner* spanner);
virtual void simulate(float deltaTime);
virtual void render(float alpha);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
virtual void render(float alpha, const glm::vec3& clipMinimum, float clipSize);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
};
/// An object with a 3D transform.
@ -521,8 +554,11 @@ public:
const QColor& getColor() const { return _color; }
virtual const QVector<AttributePointer>& getAttributes() const;
virtual bool getAttributeValues(MetavoxelInfo& info) const;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
virtual const QVector<AttributePointer>& getVoxelizedAttributes() const;
virtual bool getAttributeValues(MetavoxelInfo& info, bool force = false) const;
virtual bool blendAttributeValues(MetavoxelInfo& info, bool force = false) const;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const;
signals:
@ -538,7 +574,7 @@ private slots:
private:
void getNormal(MetavoxelInfo& info) const;
AttributeValue getNormal(MetavoxelInfo& info, int alpha) const;
QColor _color;
};
@ -555,7 +591,8 @@ public:
void setURL(const QUrl& url);
const QUrl& getURL() const { return _url; }
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize,float& distance) const;
signals:

View file

@ -32,7 +32,8 @@ private:
};
BoxSetEditVisitor::BoxSetEditVisitor(const BoxSetEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << edit.value.getAttribute()),
MetavoxelVisitor(QVector<AttributePointer>(), QVector<AttributePointer>() << edit.value.getAttribute() <<
AttributeRegistry::getInstance()->getSpannerMaskAttribute()),
_edit(edit) {
}
@ -47,25 +48,68 @@ int BoxSetEditVisitor::visit(MetavoxelInfo& info) {
float volume = (size.x * size.y * size.z) / (info.size * info.size * info.size);
if (volume >= 1.0f) {
info.outputValues[0] = _edit.value;
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<float>(1.0f));
return STOP_RECURSION; // entirely contained
}
if (info.size <= _edit.granularity) {
if (volume >= 0.5f) {
info.outputValues[0] = _edit.value;
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<float>(1.0f));
}
return STOP_RECURSION; // reached granularity limit; take best guess
}
return DEFAULT_ORDER; // subdivide
}
class GatherUnmaskedSpannersVisitor : public SpannerVisitor {
public:
GatherUnmaskedSpannersVisitor(const Box& bounds);
const QList<SharedObjectPointer>& getUnmaskedSpanners() const { return _unmaskedSpanners; }
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
private:
Box _bounds;
QList<SharedObjectPointer> _unmaskedSpanners;
};
GatherUnmaskedSpannersVisitor::GatherUnmaskedSpannersVisitor(const Box& bounds) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute()),
_bounds(bounds) {
}
bool GatherUnmaskedSpannersVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
if (!spanner->isMasked() && spanner->getBounds().intersects(_bounds)) {
_unmaskedSpanners.append(spanner);
}
return true;
}
static void setIntersectingMasked(const Box& bounds, MetavoxelData& data) {
GatherUnmaskedSpannersVisitor visitor(bounds);
data.guide(visitor);
foreach (const SharedObjectPointer& object, visitor.getUnmaskedSpanners()) {
Spanner* newSpanner = static_cast<Spanner*>(object->clone(true));
newSpanner->setMasked(true);
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), object, newSpanner);
}
}
void BoxSetEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
while (!data.getBounds().contains(region)) {
data.expand();
}
BoxSetEditVisitor visitor(*this);
data.guide(visitor);
BoxSetEditVisitor setVisitor(*this);
data.guide(setVisitor);
// flip the mask flag of all intersecting spanners
setIntersectingMasked(region, data);
}
GlobalSetEdit::GlobalSetEdit(const OwnedAttributeValue& value) :
@ -104,8 +148,56 @@ InsertSpannerEdit::InsertSpannerEdit(const AttributePointer& attribute, const Sh
spanner(spanner) {
}
class UpdateSpannerVisitor : public MetavoxelVisitor {
public:
UpdateSpannerVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
float _voxelizationSize;
int _steps;
};
UpdateSpannerVisitor::UpdateSpannerVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner) :
MetavoxelVisitor(QVector<AttributePointer>() << attributes << AttributeRegistry::getInstance()->getSpannersAttribute(),
attributes),
_spanner(spanner),
_voxelizationSize(qMax(spanner->getBounds().getLongestSide(), spanner->getPlacementGranularity()) * 2.0f /
AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()),
_steps(glm::round(logf(AttributeRegistry::getInstance()->getSpannersAttribute()->getLODThresholdMultiplier()) /
logf(2.0f) - 2.0f)) {
}
int UpdateSpannerVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_spanner->getBounds())) {
return STOP_RECURSION;
}
MetavoxelInfo* parentInfo = info.parentInfo;
for (int i = 0; i < _steps && parentInfo; i++) {
parentInfo = parentInfo->parentInfo;
}
for (int i = 0; i < _outputs.size(); i++) {
info.outputValues[i] = AttributeValue(_outputs.at(i));
}
if (parentInfo) {
foreach (const SharedObjectPointer& object,
parentInfo->inputValues.at(_outputs.size()).getInlineValue<SharedObjectSet>()) {
static_cast<const Spanner*>(object.data())->blendAttributeValues(info, true);
}
}
return (info.size > _voxelizationSize) ? DEFAULT_ORDER : STOP_RECURSION;
}
void InsertSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
data.insert(attribute, spanner);
data.insert(attribute, this->spanner);
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner);
data.guide(visitor);
}
RemoveSpannerEdit::RemoveSpannerEdit(const AttributePointer& attribute, int id) :
@ -119,21 +211,63 @@ void RemoveSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& o
qDebug() << "Missing object to remove" << id;
return;
}
// keep a strong reference to the object
SharedObjectPointer sharedPointer = object;
data.remove(attribute, object);
Spanner* spanner = static_cast<Spanner*>(object);
UpdateSpannerVisitor visitor(spanner->getVoxelizedAttributes(), spanner);
data.guide(visitor);
}
ClearSpannersEdit::ClearSpannersEdit(const AttributePointer& attribute) :
attribute(attribute) {
}
class GatherSpannerAttributesVisitor : public SpannerVisitor {
public:
GatherSpannerAttributesVisitor(const AttributePointer& attribute);
const QSet<AttributePointer>& getAttributes() const { return _attributes; }
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
protected:
QSet<AttributePointer> _attributes;
};
GatherSpannerAttributesVisitor::GatherSpannerAttributesVisitor(const AttributePointer& attribute) :
SpannerVisitor(QVector<AttributePointer>() << attribute) {
}
bool GatherSpannerAttributesVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
foreach (const AttributePointer& attribute, spanner->getVoxelizedAttributes()) {
_attributes.insert(attribute);
}
return true;
}
void ClearSpannersEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// find all the spanner attributes
GatherSpannerAttributesVisitor visitor(attribute);
data.guide(visitor);
data.clear(attribute);
foreach (const AttributePointer& attribute, visitor.getAttributes()) {
data.clear(attribute);
}
}
SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) :
spanner(spanner) {
}
class SetSpannerEditVisitor : public MetavoxelVisitor {
public:
SetSpannerEditVisitor(Spanner* spanner);
SetSpannerEditVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner);
virtual int visit(MetavoxelInfo& info);
@ -142,17 +276,20 @@ private:
Spanner* _spanner;
};
SetSpannerEditVisitor::SetSpannerEditVisitor(Spanner* spanner) :
MetavoxelVisitor(QVector<AttributePointer>(), spanner->getAttributes()),
SetSpannerEditVisitor::SetSpannerEditVisitor(const QVector<AttributePointer>& attributes, Spanner* spanner) :
MetavoxelVisitor(attributes, QVector<AttributePointer>() << attributes <<
AttributeRegistry::getInstance()->getSpannerMaskAttribute()),
_spanner(spanner) {
}
int SetSpannerEditVisitor::visit(MetavoxelInfo& info) {
return _spanner->getAttributeValues(info) ? DEFAULT_ORDER : STOP_RECURSION;
}
SetSpannerEdit::SetSpannerEdit(const SharedObjectPointer& spanner) :
spanner(spanner) {
if (_spanner->blendAttributeValues(info)) {
return DEFAULT_ORDER;
}
if (info.outputValues.at(0).getAttribute()) {
info.outputValues.last() = AttributeValue(_outputs.last(), encodeInline<float>(1.0f));
}
return STOP_RECURSION;
}
void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
@ -163,6 +300,8 @@ void SetSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& obje
data.expand();
}
SetSpannerEditVisitor visitor(spanner);
SetSpannerEditVisitor visitor(spanner->getAttributes(), spanner);
data.guide(visitor);
setIntersectingMasked(spanner->getBounds(), data);
}

View file

@ -299,6 +299,11 @@ Box operator*(const glm::mat4& matrix, const Box& box) {
return newBox;
}
QDebug& operator<<(QDebug& out, const Box& box) {
return out << '(' << box.minimum.x << box.minimum.y << box.minimum.z << ") (" <<
box.maximum.x << box.maximum.y << box.maximum.z << ')';
}
QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) {
QVBoxLayout* layout = new QVBoxLayout();
layout->setContentsMargins(QMargins());

View file

@ -60,6 +60,8 @@ DECLARE_STREAMABLE_METATYPE(Box)
Box operator*(const glm::mat4& matrix, const Box& box);
QDebug& operator<<(QDebug& out, const Box& box);
/// Editor for meta-object values.
class QMetaObjectEditor : public QWidget {
Q_OBJECT

View file

@ -37,7 +37,7 @@ void SharedObject::decrementReferenceCount() {
}
}
SharedObject* SharedObject::clone() const {
SharedObject* SharedObject::clone(bool withID) const {
// default behavior is to make a copy using the no-arg constructor and copy the stored properties
const QMetaObject* metaObject = this->metaObject();
SharedObject* newObject = static_cast<SharedObject*>(metaObject->newInstance());
@ -50,6 +50,9 @@ SharedObject* SharedObject::clone() const {
foreach (const QByteArray& propertyName, dynamicPropertyNames()) {
newObject->setProperty(propertyName, property(propertyName));
}
if (withID) {
newObject->setID(_id);
}
return newObject;
}
@ -91,6 +94,11 @@ void SharedObject::dump(QDebug debug) const {
}
}
void SharedObject::setID(int id) {
_weakHash.remove(_id);
_weakHash.insert(_id = id, this);
}
int SharedObject::_lastID = 0;
WeakSharedObjectHash SharedObject::_weakHash;

View file

@ -47,7 +47,8 @@ public:
void decrementReferenceCount();
/// Creates a new clone of this object.
virtual SharedObject* clone() const;
/// \param withID if true, give the clone the same ID as this object
virtual SharedObject* clone(bool withID = false) const;
/// Tests this object for equality with another.
virtual bool equals(const SharedObject* other) const;
@ -57,6 +58,8 @@ public:
private:
void setID(int id);
int _id;
int _remoteID;
int _referenceCount;

View file

@ -34,9 +34,103 @@ static int streamedBytesReceived = 0;
static int sharedObjectsCreated = 0;
static int sharedObjectsDestroyed = 0;
static QByteArray createRandomBytes(int minimumSize, int maximumSize) {
QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0);
for (int i = 0; i < bytes.size(); i++) {
bytes[i] = rand();
}
return bytes;
}
static QByteArray createRandomBytes() {
const int MIN_BYTES = 4;
const int MAX_BYTES = 16;
return createRandomBytes(MIN_BYTES, MAX_BYTES);
}
static TestMessageC createRandomMessageC() {
TestMessageC message;
message.foo = randomBoolean();
message.bar = rand();
message.baz = randFloat();
message.bong.foo = createRandomBytes();
return message;
}
static bool testSerialization(Bitstream::MetadataType metadataType) {
QByteArray array;
QDataStream outStream(&array, QIODevice::WriteOnly);
Bitstream out(outStream, metadataType);
SharedObjectPointer testObjectWrittenA = new TestSharedObjectA(randFloat());
out << testObjectWrittenA;
SharedObjectPointer testObjectWrittenB = new TestSharedObjectB(randFloat(), createRandomBytes());
out << testObjectWrittenB;
TestMessageC messageWritten = createRandomMessageC();
out << QVariant::fromValue(messageWritten);
QByteArray endWritten = "end";
out << endWritten;
out.flush();
QDataStream inStream(array);
Bitstream in(inStream, metadataType);
in.addMetaObjectSubstitution("TestSharedObjectA", &TestSharedObjectB::staticMetaObject);
in.addMetaObjectSubstitution("TestSharedObjectB", &TestSharedObjectA::staticMetaObject);
in.addTypeSubstitution("TestMessageC", TestMessageA::Type);
SharedObjectPointer testObjectReadA;
in >> testObjectReadA;
if (!testObjectReadA || testObjectReadA->metaObject() != &TestSharedObjectB::staticMetaObject) {
qDebug() << "Wrong class for A" << testObjectReadA << metadataType;
return true;
}
if (metadataType == Bitstream::FULL_METADATA && static_cast<TestSharedObjectA*>(testObjectWrittenA.data())->getFoo() !=
static_cast<TestSharedObjectB*>(testObjectReadA.data())->getFoo()) {
QDebug debug = qDebug() << "Failed to transfer shared field from A to B";
testObjectWrittenA->dump(debug);
testObjectReadA->dump(debug);
return true;
}
SharedObjectPointer testObjectReadB;
in >> testObjectReadB;
if (!testObjectReadB || testObjectReadB->metaObject() != &TestSharedObjectA::staticMetaObject) {
qDebug() << "Wrong class for B" << testObjectReadB << metadataType;
return true;
}
if (metadataType == Bitstream::FULL_METADATA && static_cast<TestSharedObjectB*>(testObjectWrittenB.data())->getFoo() !=
static_cast<TestSharedObjectA*>(testObjectReadB.data())->getFoo()) {
QDebug debug = qDebug() << "Failed to transfer shared field from B to A";
testObjectWrittenB->dump(debug);
testObjectReadB->dump(debug);
return true;
}
QVariant messageRead;
in >> messageRead;
if (!messageRead.isValid() || messageRead.userType() != TestMessageA::Type) {
qDebug() << "Wrong type for message" << messageRead;
return true;
}
if (metadataType == Bitstream::FULL_METADATA && messageWritten.foo != messageRead.value<TestMessageA>().foo) {
QDebug debug = qDebug() << "Failed to transfer shared field between messages" <<
messageWritten.foo << messageRead.value<TestMessageA>().foo;
return true;
}
QByteArray endRead;
in >> endRead;
if (endWritten != endRead) {
qDebug() << "End tag mismatch." << endRead;
return true;
}
return false;
}
bool MetavoxelTests::run() {
qDebug() << "Running metavoxel tests...";
qDebug() << "Running transmission tests...";
qDebug();
// seed the random number generator so that our tests are reproducible
srand(0xBAAAAABE);
@ -62,26 +156,20 @@ bool MetavoxelTests::run() {
qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived;
qDebug() << "Sent" << datagramsSent << "datagrams, received" << datagramsReceived;
qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed;
qDebug();
qDebug() << "Running serialization tests...";
qDebug();
if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) {
return true;
}
qDebug() << "All tests passed!";
return false;
}
static QByteArray createRandomBytes(int minimumSize, int maximumSize) {
QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0);
for (int i = 0; i < bytes.size(); i++) {
bytes[i] = rand();
}
return bytes;
}
static QByteArray createRandomBytes() {
const int MIN_BYTES = 4;
const int MAX_BYTES = 16;
return createRandomBytes(MIN_BYTES, MAX_BYTES);
}
static SharedObjectPointer createRandomSharedObject() {
switch (randIntInRange(0, 2)) {
case 0: return new TestSharedObjectA(randFloat());
@ -132,12 +220,7 @@ static QVariant createRandomMessage() {
}
case 2:
default: {
TestMessageC message;
message.foo = randomBoolean();
message.bar = rand();
message.baz = randFloat();
message.bong.foo = createRandomBytes();
return QVariant::fromValue(message);
return QVariant::fromValue(createRandomMessageC());
}
}
}
@ -322,7 +405,9 @@ void TestSharedObjectA::setFoo(float foo) {
}
}
TestSharedObjectB::TestSharedObjectB() {
TestSharedObjectB::TestSharedObjectB(float foo, const QByteArray& bar) :
_foo(foo),
_bar(bar) {
sharedObjectsCreated++;
}

View file

@ -89,11 +89,24 @@ private:
/// Another simple shared object.
class TestSharedObjectB : public SharedObject {
Q_OBJECT
Q_PROPERTY(float foo READ getFoo WRITE setFoo)
Q_PROPERTY(QByteArray bar READ getBar WRITE setBar)
public:
Q_INVOKABLE TestSharedObjectB();
Q_INVOKABLE TestSharedObjectB(float foo = 0.0f, const QByteArray& bar = QByteArray());
virtual ~TestSharedObjectB();
void setFoo(float foo) { _foo = foo; }
float getFoo() const { return _foo; }
void setBar(const QByteArray& bar) { _bar = bar; }
const QByteArray& getBar() const { return _bar; }
private:
float _foo;
QByteArray _bar;
};
/// A simple test message.

View file

@ -24,10 +24,16 @@ public:
QStringList bases;
};
class Field {
public:
QString type;
QString name;
};
class Streamable {
public:
Class clazz;
QStringList fields;
QList<Field> fields;
};
void processInput(QTextStream& in, QList<Streamable>* streamables) {
@ -66,8 +72,10 @@ void processInput(QTextStream& in, QList<Streamable>* streamables) {
} else if (match.startsWith("STREAM")) {
match.chop(1); // get rid of the semicolon
match = match.trimmed(); // and any space before it
currentStreamable.fields.append(match.mid(match.lastIndexOf(' ') + 1));
match = match.mid(match.indexOf(' ') + 1).trimmed(); // and STREAM, and any space before it
int index = match.lastIndexOf(' ');
Field field = { match.left(index).simplified(), match.mid(index + 1) };
currentStreamable.fields.append(field);
} else { // match.startsWith("class")
classExp.exactMatch(match);
@ -90,12 +98,85 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
foreach (const Streamable& str, streamables) {
const QString& name = str.clazz.name;
out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n";
out << "const QVector<MetaField>& " << name << "::getMetaFields() {\n";
out << " static QVector<MetaField> metaFields = QVector<MetaField>()";
foreach (const QString& base, str.clazz.bases) {
out << " << " << base << "::getMetaFields()";
}
foreach (const Field& field, str.fields) {
out << "\n << MetaField(\"" << field.name << "\", Bitstream::getTypeStreamer(qMetaTypeId<" <<
field.type << ">()))";
}
out << ";\n";
out << " return metaFields;\n";
out << "}\n";
out << "int " << name << "::getFieldIndex(const QByteArray& name) {\n";
out << " static QHash<QByteArray, int> fieldIndices = createFieldIndices();\n";
out << " return fieldIndices.value(name) - 1;\n";
out << "}\n";
out << "QHash<QByteArray, int> " << name << "::createFieldIndices() {\n";
out << " QHash<QByteArray, int> indices;\n";
out << " int index = 1;\n";
foreach (const QString& base, str.clazz.bases) {
out << " foreach (const MetaField& field, " << base << "::getMetaFields()) {\n";
out << " indices.insert(field.getName(), index++);\n";
out << " }\n";
}
out << " foreach (const MetaField& field, getMetaFields()) {\n";
out << " indices.insert(field.getName(), index++);\n";
out << " }\n";
out << " return indices;\n";
out << "}\n";
out << "void " << name << "::setField(int index, const QVariant& value) {\n";
if (!str.clazz.bases.isEmpty()) {
out << " int nextIndex;\n";
}
foreach (const QString& base, str.clazz.bases) {
out << " if ((nextIndex = index - " << base << "::getMetaFields().size()) < 0) {\n";
out << " " << base << "::setField(index, value);\n";
out << " return;\n";
out << " }\n";
out << " index = nextIndex;\n";
}
out << " switch (index) {\n";
for (int i = 0; i < str.fields.size(); i++) {
out << " case " << i << ":\n";
out << " this->" << str.fields.at(i).name << " = value.value<" << str.fields.at(i).type << ">();\n";
out << " break;\n";
}
out << " }\n";
out << "}\n";
out << "QVariant " << name << "::getField(int index) const {\n";
if (!str.clazz.bases.isEmpty()) {
out << " int nextIndex;\n";
}
foreach (const QString& base, str.clazz.bases) {
out << " if ((nextIndex = index - " << base << "::getMetaFields().size()) < 0) {\n";
out << " return " << base << "::getField(index);\n";
out << " }\n";
out << " index = nextIndex;\n";
}
out << " switch (index) {\n";
for (int i = 0; i < str.fields.size(); i++) {
out << " case " << i << ":\n";
out << " return QVariant::fromValue(this->" << str.fields.at(i).name << ");\n";
}
out << " }\n";
out << " return QVariant();\n";
out << "}\n";
out << "Bitstream& operator<<(Bitstream& out, const " << name << "& obj) {\n";
foreach (const QString& base, str.clazz.bases) {
out << " out << static_cast<const " << base << "&>(obj);\n";
}
foreach (const QString& field, str.fields) {
out << " out << obj." << field << ";\n";
foreach (const Field& field, str.fields) {
out << " out << obj." << field.name << ";\n";
}
out << " return out;\n";
out << "}\n";
@ -104,12 +185,32 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
foreach (const QString& base, str.clazz.bases) {
out << " in >> static_cast<" << base << "&>(obj);\n";
}
foreach (const QString& field, str.fields) {
out << " in >> obj." << field << ";\n";
foreach (const Field& field, str.fields) {
out << " in >> obj." << field.name << ";\n";
}
out << " return in;\n";
out << "}\n";
out << "template<> void Bitstream::writeRawDelta(const " << name << "& value, const " << name << "& reference) {\n";
foreach (const QString& base, str.clazz.bases) {
out << " writeRawDelta(static_cast<const " << base << "&>(value), static_cast<const " <<
base << "&>(reference));\n";
}
foreach (const Field& field, str.fields) {
out << " writeDelta(value." << field.name << ", reference." << field.name << ");\n";
}
out << "}\n";
out << "template<> void Bitstream::readRawDelta(" << name << "& value, const " << name << "& reference) {\n";
foreach (const QString& base, str.clazz.bases) {
out << " readRawDelta(static_cast<" << base << "&>(value), static_cast<const " <<
base << "&>(reference));\n";
}
foreach (const Field& field, str.fields) {
out << " readDelta(value." << field.name << ", reference." << field.name << ");\n";
}
out << "}\n";
out << "bool operator==(const " << name << "& first, const " << name << "& second) {\n";
if (str.clazz.bases.isEmpty() && str.fields.isEmpty()) {
out << " return true";
@ -124,12 +225,12 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
out << "static_cast<const " << base << "&>(first) == static_cast<const " << base << "&>(second)";
first = false;
}
foreach (const QString& field, str.fields) {
foreach (const Field& field, str.fields) {
if (!first) {
out << " &&\n";
out << " ";
}
out << "first." << field << " == second." << field;
out << "first." << field.name << " == second." << field.name;
first = false;
}
}
@ -150,19 +251,17 @@ void generateOutput (QTextStream& out, const QList<Streamable>& streamables) {
out << "static_cast<const " << base << "&>(first) != static_cast<const " << base << "&>(second)";
first = false;
}
foreach (const QString& field, str.fields) {
foreach (const Field& field, str.fields) {
if (!first) {
out << " ||\n";
out << " ";
}
out << "first." << field << " != second." << field;
out << "first." << field.name << " != second." << field.name;
first = false;
}
}
out << ";\n";
out << "}\n";
out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n\n";
out << "}\n\n";
}
}