mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 07:34:02 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
Conflicts: interface/interface_en.ts
This commit is contained in:
commit
ca863e77f4
26 changed files with 2467 additions and 379 deletions
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 ®istry;
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -420,7 +420,7 @@ bool CircularBuffer::atEnd() const {
|
|||
}
|
||||
|
||||
qint64 CircularBuffer::bytesAvailable() const {
|
||||
return _size - _offset + QIODevice::bytesAvailable();
|
||||
return _size - _offset;
|
||||
}
|
||||
|
||||
bool CircularBuffer::canReadLine() const {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue