Merge branch 'fix_attachment_bug_with_recording' of github.com:Atlante45/hifi into radio_js

Conflicts:
	libraries/audio/src/AudioInjector.cpp
This commit is contained in:
Atlante45 2014-09-26 10:45:34 -07:00
commit a4a5d71caa
14 changed files with 433 additions and 89 deletions

View file

@ -75,7 +75,7 @@ InboundAudioStream::Settings AudioMixer::_streamSettings;
bool AudioMixer::_printStreamStats = false;
bool AudioMixer::_enableFilter = false;
bool AudioMixer::_enableFilter = true;
AudioMixer::AudioMixer(const QByteArray& packet) :
ThreadedAssignment(packet),
@ -710,7 +710,9 @@ void AudioMixer::run() {
}
const QString FILTER_KEY = "J-enable-filter";
_enableFilter = audioGroupObject[FILTER_KEY].toBool();
if (audioGroupObject[FILTER_KEY].isBool()) {
_enableFilter = audioGroupObject[FILTER_KEY].toBool();
}
if (_enableFilter) {
qDebug() << "Filter enabled";
}

View file

@ -67,7 +67,7 @@
"type": "checkbox",
"label": "Enable Positional Filter",
"help": "If enabled, positional audio stream uses lowpass filter",
"default": false
"default": true
}
}
}

View file

@ -40,6 +40,7 @@ var timerOffset;
setupToolBar();
var timer = null;
var slider = null;
setupTimer();
var watchStop = false;
@ -115,6 +116,30 @@ function setupTimer() {
alpha: 1.0,
visible: true
});
slider = { x: 0, y: 0,
w: 200, h: 20,
pos: 0.0, // 0.0 <= pos <= 1.0
};
slider.background = Overlays.addOverlay("text", {
text: "",
backgroundColor: { red: 128, green: 128, blue: 128 },
x: slider.x, y: slider.y,
width: slider.w,
height: slider.h,
alpha: 1.0,
visible: true
});
slider.foreground = Overlays.addOverlay("text", {
text: "",
backgroundColor: { red: 200, green: 200, blue: 200 },
x: slider.x, y: slider.y,
width: slider.pos * slider.w,
height: slider.h,
alpha: 1.0,
visible: true
});
}
function updateTimer() {
@ -131,6 +156,16 @@ function updateTimer() {
text: text
})
toolBar.changeSpacing(text.length * 8 + ((MyAvatar.isRecording()) ? 15 : 0), spacing);
if (MyAvatar.isRecording()) {
slider.pos = 1.0;
} else if (MyAvatar.playerLength() > 0) {
slider.pos = MyAvatar.playerElapsed() / MyAvatar.playerLength();
}
Overlays.editOverlay(slider.foreground, {
width: slider.pos * slider.w
});
}
function formatTime(time) {
@ -163,7 +198,19 @@ function moveUI() {
Overlays.editOverlay(timer, {
x: relative.x + timerOffset - ToolBar.SPACING,
y: windowDimensions.y - relative.y - ToolBar.SPACING
});
});
slider.x = relative.x - ToolBar.SPACING;
slider.y = windowDimensions.y - relative.y - slider.h - ToolBar.SPACING;
Overlays.editOverlay(slider.background, {
x: slider.x,
y: slider.y,
});
Overlays.editOverlay(slider.foreground, {
x: slider.x,
y: slider.y,
});
}
function mousePressEvent(event) {
@ -188,7 +235,7 @@ function mousePressEvent(event) {
}
} else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) {
if (MyAvatar.isPlaying()) {
MyAvatar.stopPlaying();
MyAvatar.pausePlayer();
toolBar.setAlpha(ALPHA_ON, recordIcon);
toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon);
@ -203,7 +250,7 @@ function mousePressEvent(event) {
}
} else if (playLoopIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) {
if (MyAvatar.isPlaying()) {
MyAvatar.stopPlaying();
MyAvatar.pausePlayer();
toolBar.setAlpha(ALPHA_ON, recordIcon);
toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon);
@ -234,10 +281,30 @@ function mousePressEvent(event) {
toolBar.setAlpha(ALPHA_ON, saveIcon);
}
}
} else {
} else if (MyAvatar.playerLength() > 0 &&
slider.x < event.x && event.x < slider.x + slider.w &&
slider.y < event.y && event.y < slider.y + slider.h) {
isSliding = true;
slider.pos = (event.x - slider.x) / slider.w;
MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength());
}
}
var isSliding = false;
function mouseMoveEvent(event) {
if (isSliding) {
slider.pos = (event.x - slider.x) / slider.w;
if (slider.pos < 0.0 || slider.pos > 1.0) {
MyAvatar.stopPlaying();
slider.pos = 0.0;
}
MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength());
}
}
function mouseReleaseEvent(event) {
isSliding = false;
}
function update() {
var newDimensions = Controller.getViewportDimensions();
@ -264,11 +331,15 @@ function scriptEnding() {
if (MyAvatar.isPlaying()) {
MyAvatar.stopPlaying();
}
toolBar.cleanup();
Overlays.deleteOverlay(timer);
toolBar.cleanup();
Overlays.deleteOverlay(timer);
Overlays.deleteOverlay(slider.background);
Overlays.deleteOverlay(slider.foreground);
}
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);

View file

@ -1127,8 +1127,6 @@ function keyPressEvent(event) {
} else if (event.text == "z") {
undoSound.playRandom();
}
}
trackKeyPressEvent(event); // used by preview support

View file

@ -17,13 +17,20 @@ const int SPLAT_COUNT = 4;
// the splat textures
uniform sampler2D diffuseMaps[SPLAT_COUNT];
// the model space normal
varying vec3 normal;
// alpha values for the four splat textures
varying vec4 alphaValues;
void main(void) {
// determine the cube face to use for texture coordinate generation
vec3 absNormal = abs(normal);
vec2 parameters = step(absNormal.yy, absNormal.xz) * step(absNormal.zx, absNormal.xz);
// blend the splat textures
gl_FragColor = (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y +
texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z +
texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w);
gl_FragColor = (texture2D(diffuseMaps[0], mix(gl_TexCoord[0].xw, gl_TexCoord[0].zy, parameters)) * alphaValues.x +
texture2D(diffuseMaps[1], mix(gl_TexCoord[1].xw, gl_TexCoord[1].zy, parameters)) * alphaValues.y +
texture2D(diffuseMaps[2], mix(gl_TexCoord[2].xw, gl_TexCoord[2].zy, parameters)) * alphaValues.z +
texture2D(diffuseMaps[3], mix(gl_TexCoord[3].xw, gl_TexCoord[3].zy, parameters)) * alphaValues.w);
}

View file

@ -29,6 +29,9 @@ attribute vec4 materials;
// the weights of each material
attribute vec4 materialWeights;
// the model space normal
varying vec3 normal;
// alpha values for the four splat textures
varying vec4 alphaValues;
@ -36,12 +39,19 @@ void main(void) {
// use the fixed-function position
gl_Position = ftransform();
// pass along the normal
normal = gl_Normal;
// pass along the scaled/offset texture coordinates
vec4 textureSpacePosition = vec4(gl_Vertex.xz, 0.0, 1.0);
gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0], 0.0, 1.0);
gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1], 0.0, 1.0);
gl_TexCoord[2] = textureSpacePosition * vec4(splatTextureScalesS[2], splatTextureScalesT[2], 0.0, 1.0);
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
vec4 textureSpacePosition = gl_Vertex.xyyz;
gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0],
splatTextureScalesS[0], splatTextureScalesT[0]);
gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1],
splatTextureScalesS[1], splatTextureScalesT[1]);
gl_TexCoord[2] = textureSpacePosition * vec4(splatTextureScalesS[2], splatTextureScalesT[2],
splatTextureScalesS[2], splatTextureScalesT[2]);
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3],
splatTextureScalesS[3], splatTextureScalesT[3]);
// compute the alpha values for each texture
float value = materials[0];

View file

@ -25,7 +25,8 @@ AudioInjector::AudioInjector(QObject* parent) :
QObject(parent),
_sound(NULL),
_options(),
_shouldStop(false)
_shouldStop(false),
_currentSendPosition(0)
{
}
@ -97,17 +98,15 @@ void AudioInjector::injectAudio() {
timer.start();
int nextFrame = 0;
int currentSendPosition = 0;
int numPreAudioDataBytes = injectAudioPacket.size();
bool shouldLoop = _options.getLoop();
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
quint16 outgoingInjectedAudioSequenceNumber = 0;
while (currentSendPosition < soundByteArray.size() && !_shouldStop) {
while (_currentSendPosition < soundByteArray.size() && !_shouldStop) {
int bytesToCopy = std::min(((_options.isStereo()) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL,
soundByteArray.size() - currentSendPosition);
soundByteArray.size() - _currentSendPosition);
memcpy(injectAudioPacket.data() + positionOptionOffset,
&_options.getPosition(),
sizeof(_options.getPosition()));
@ -124,7 +123,7 @@ void AudioInjector::injectAudio() {
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
memcpy(injectAudioPacket.data() + numPreAudioDataBytes,
soundByteArray.data() + currentSendPosition, bytesToCopy);
soundByteArray.data() + _currentSendPosition, bytesToCopy);
// grab our audio mixer from the NodeList, if it exists
NodeList* nodeList = NodeList::getInstance();
@ -134,22 +133,22 @@ void AudioInjector::injectAudio() {
nodeList->writeDatagram(injectAudioPacket, audioMixer);
outgoingInjectedAudioSequenceNumber++;
currentSendPosition += bytesToCopy;
_currentSendPosition += bytesToCopy;
// send two packets before the first sleep so the mixer can start playback right away
if (currentSendPosition != bytesToCopy && currentSendPosition < soundByteArray.size()) {
if (_currentSendPosition != bytesToCopy && _currentSendPosition < soundByteArray.size()) {
// not the first packet and not done
// sleep for the appropriate time
int usecToSleep = (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - timer.nsecsElapsed() / 1000;
if (usecToSleep > 0) {
usleep(usecToSleep);
}
}
}
if (shouldLoop && currentSendPosition == soundByteArray.size()) {
currentSendPosition = 0;
if (shouldLoop && _currentSendPosition >= soundByteArray.size()) {
_currentSendPosition = 0;
}
}
}

View file

@ -26,16 +26,20 @@ class AudioInjector : public QObject {
public:
AudioInjector(QObject* parent);
AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions);
int getCurrentSendPosition() const { return _currentSendPosition; }
public slots:
void injectAudio();
void stop() { _shouldStop = true; }
void setOptions(AudioInjectorOptions& options);
void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; }
signals:
void finished();
private:
Sound* _sound;
AudioInjectorOptions _options;
bool _shouldStop;
int _currentSendPosition;
};
Q_DECLARE_METATYPE(AudioInjector*)

View file

@ -587,18 +587,13 @@ bool AvatarData::hasReferential() {
}
bool AvatarData::isPlaying() {
if (!_player) {
return false;
}
if (QThread::currentThread() != thread()) {
bool result;
QMetaObject::invokeMethod(this, "isPlaying", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result));
return result;
}
return _player && _player->isPlaying();
}
bool AvatarData::isPaused() {
return _player && _player->isPaused();
}
qint64 AvatarData::playerElapsed() {
if (!_player) {
return 0;
@ -625,6 +620,14 @@ qint64 AvatarData::playerLength() {
return _player->getRecording()->getLength();
}
int AvatarData::playerCurrentFrame() {
return (_player) ? _player->getCurrentFrame() : 0;
}
int AvatarData::playerFrameNumber() {
return (_player && _player->getRecording()) ? _player->getRecording()->getFrameNumber() : 0;
}
void AvatarData::loadRecording(QString filename) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection,
@ -649,6 +652,18 @@ void AvatarData::startPlaying() {
_player->startPlaying();
}
void AvatarData::setPlayerFrame(int frame) {
if (_player) {
_player->setCurrentFrame(frame);
}
}
void AvatarData::setPlayerTime(qint64 time) {
if (_player) {
_player->setCurrentTime(time);
}
}
void AvatarData::setPlayFromCurrentLocation(bool playFromCurrentLocation) {
if (_player) {
_player->setPlayFromCurrentLocation(playFromCurrentLocation);
@ -696,6 +711,19 @@ void AvatarData::play() {
}
}
void AvatarData::pausePlayer() {
if (!_player) {
return;
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection);
return;
}
if (_player) {
_player->pausePlayer();
}
}
void AvatarData::stopPlaying() {
if (!_player) {
return;

View file

@ -296,10 +296,16 @@ public slots:
bool hasReferential();
bool isPlaying();
bool isPaused();
qint64 playerElapsed();
qint64 playerLength();
int playerCurrentFrame();
int playerFrameNumber();
void loadRecording(QString filename);
void startPlaying();
void setPlayerFrame(int frame);
void setPlayerTime(qint64 time);
void setPlayFromCurrentLocation(bool playFromCurrentLocation);
void setPlayerLoop(bool loop);
void setPlayerUseDisplayName(bool useDisplayName);
@ -307,6 +313,7 @@ public slots:
void setPlayerUseHeadModel(bool useHeadModel);
void setPlayerUseSkeletonModel(bool useSkeletonModel);
void play();
void pausePlayer();
void stopPlaying();
protected:

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <AudioRingBuffer.h>
#include <GLMHelpers.h>
#include <NodeList.h>
#include <StreamUtils.h>
@ -18,6 +19,8 @@
Player::Player(AvatarData* avatar) :
_recording(new Recording()),
_pausedFrame(-1),
_timerOffset(0),
_avatar(avatar),
_audioThread(NULL),
_playFromCurrentPosition(true),
@ -36,16 +39,26 @@ bool Player::isPlaying() const {
return _timer.isValid();
}
bool Player::isPaused() const {
return (_pausedFrame != -1);
}
qint64 Player::elapsed() const {
if (isPlaying()) {
return _timer.elapsed();
return _timerOffset + _timer.elapsed();
} else if (isPaused()) {
return _timerOffset;
} else {
return 0;
}
}
void Player::startPlaying() {
if (_recording && _recording->getFrameNumber() > 0) {
if (!_recording || _recording->getFrameNumber() <= 1) {
return;
}
if (!isPaused()) {
_currentContext.globalTimestamp = usecTimestampNow();
_currentContext.domain = NodeList::getInstance()->getDomainHandler().getHostname();
_currentContext.position = _avatar->getPosition();
@ -97,9 +110,17 @@ void Player::startPlaying() {
_avatar->setForceFaceshiftConnected(true);
qDebug() << "Recorder::startPlaying()";
setupAudioThread();
_currentFrame = 0;
_timerOffset = 0;
_timer.start();
} else {
qDebug() << "Recorder::startPlaying(): Unpause";
setupAudioThread();
_timer.start();
setCurrentFrame(_pausedFrame);
_pausedFrame = -1;
}
}
@ -107,6 +128,7 @@ void Player::stopPlaying() {
if (!isPlaying()) {
return;
}
_pausedFrame = -1;
_timer.invalidate();
cleanupAudioThread();
_avatar->clearJointsData();
@ -130,6 +152,15 @@ void Player::stopPlaying() {
qDebug() << "Recorder::stopPlaying()";
}
void Player::pausePlayer() {
_timerOffset = elapsed();
_timer.invalidate();
cleanupAudioThread();
_pausedFrame = _currentFrame;
qDebug() << "Recorder::pausePlayer()";
}
void Player::setupAudioThread() {
_audioThread = new QThread();
_options.setPosition(_avatar->getPosition());
@ -156,6 +187,7 @@ void Player::loopRecording() {
cleanupAudioThread();
setupAudioThread();
_currentFrame = 0;
_timerOffset = 0;
_timer.restart();
}
@ -166,10 +198,13 @@ void Player::loadFromFile(const QString& file) {
_recording = RecordingPointer(new Recording());
}
readRecordingFromFile(_recording, file);
_pausedFrame = -1;
}
void Player::loadRecording(RecordingPointer recording) {
_recording = recording;
_pausedFrame = -1;
}
void Player::play() {
@ -213,6 +248,77 @@ void Player::play() {
_injector->setOptions(_options);
}
void Player::setCurrentFrame(int currentFrame) {
if (_recording && (currentFrame < 0 || currentFrame >= _recording->getFrameNumber())) {
stopPlaying();
return;
}
_currentFrame = currentFrame;
_timerOffset = _recording->getFrameTimestamp(_currentFrame);
if (isPlaying()) {
_timer.start();
setAudionInjectorPosition();
} else {
_pausedFrame = currentFrame;
}
}
void Player::setCurrentTime(qint64 currentTime) {
if (currentTime < 0 || currentTime >= _recording->getLength()) {
stopPlaying();
return;
}
// Find correct frame
int lowestBound = 0;
int highestBound = _recording->getFrameNumber() - 1;
while (lowestBound + 1 != highestBound) {
assert(lowestBound < highestBound);
int bestGuess = lowestBound +
(highestBound - lowestBound) *
(float)(currentTime - _recording->getFrameTimestamp(lowestBound)) /
(float)(_recording->getFrameTimestamp(highestBound) - _recording->getFrameTimestamp(lowestBound));
if (_recording->getFrameTimestamp(bestGuess) <= currentTime) {
if (currentTime < _recording->getFrameTimestamp(bestGuess + 1)) {
lowestBound = bestGuess;
highestBound = bestGuess + 1;
} else {
lowestBound = bestGuess + 1;
}
} else {
if (_recording->getFrameTimestamp(bestGuess - 1) <= currentTime) {
lowestBound = bestGuess - 1;
highestBound = bestGuess;
} else {
highestBound = bestGuess - 1;
}
}
}
_currentFrame = lowestBound;
_timerOffset = _recording->getFrameTimestamp(lowestBound);
if (isPlaying()) {
_timer.start();
setAudionInjectorPosition();
} else {
_pausedFrame = lowestBound;
}
}
void Player::setAudionInjectorPosition() {
int MSEC_PER_SEC = 1000;
int SAMPLE_SIZE = 2; // 16 bits
int CHANNEL_COUNT = 1;
int FRAME_SIZE = SAMPLE_SIZE * CHANNEL_COUNT;
int currentAudioFrame = elapsed() * FRAME_SIZE * (SAMPLE_RATE / MSEC_PER_SEC);
_injector->setCurrentSendPosition(currentAudioFrame);
}
void Player::setPlayFromCurrentLocation(bool playFromCurrentLocation) {
_playFromCurrentPosition = playFromCurrentLocation;
}
@ -227,7 +333,7 @@ bool Player::computeCurrentFrame() {
}
while (_currentFrame < _recording->getFrameNumber() - 1 &&
_recording->getFrameTimestamp(_currentFrame) < _timer.elapsed()) {
_recording->getFrameTimestamp(_currentFrame) < elapsed()) {
++_currentFrame;
}

View file

@ -30,17 +30,23 @@ public:
Player(AvatarData* avatar);
bool isPlaying() const;
bool isPaused() const;
qint64 elapsed() const;
RecordingPointer getRecording() const { return _recording; }
int getCurrentFrame() const { return _currentFrame; }
public slots:
void startPlaying();
void stopPlaying();
void pausePlayer();
void loadFromFile(const QString& file);
void loadRecording(RecordingPointer recording);
void play();
void setCurrentFrame(int currentFrame);
void setCurrentTime(qint64 currentTime);
void setPlayFromCurrentLocation(bool playFromCurrentPosition);
void setLoop(bool loop) { _loop = loop; }
void useAttachements(bool useAttachments) { _useAttachments = useAttachments; }
@ -52,11 +58,14 @@ private:
void setupAudioThread();
void cleanupAudioThread();
void loopRecording();
void setAudionInjectorPosition();
bool computeCurrentFrame();
QElapsedTimer _timer;
RecordingPointer _recording;
int _currentFrame;
int _pausedFrame;
qint64 _timerOffset;
QSharedPointer<AudioInjector> _injector;
AudioInjectorOptions _options;
@ -64,7 +73,6 @@ private:
AvatarData* _avatar;
QThread* _audioThread;
RecordingContext _currentContext;
bool _playFromCurrentPosition;
bool _loop;
@ -72,7 +80,6 @@ private:
bool _useDisplayName;
bool _useHeadURL;
bool _useSkeletonURL;
};
#endif // hifi_Player_h

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <AudioRingBuffer.h>
#include <GLMHelpers.h>
#include <NetworkAccessManager.h>
#include <NodeList.h>
@ -60,6 +61,9 @@ qint32 Recording::getFrameTimestamp(int i) const {
if (i >= _timestamps.size()) {
return getLength();
}
if (i < 0) {
return 0;
}
return _timestamps[i];
}
@ -781,7 +785,6 @@ RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QStr
fileStream >> audioArray;
// Cut down audio if necessary
int SAMPLE_RATE = 48000; // 48 kHz
int SAMPLE_SIZE = 2; // 16 bits
int MSEC_PER_SEC = 1000;
int audioLength = recording->getLength() * SAMPLE_SIZE * (SAMPLE_RATE / MSEC_PER_SEC);

View file

@ -667,8 +667,9 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
QVector<QRgb> colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
QVector<QRgb> oldColorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
colorPointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME);
QVector<QRgb> colorContents = oldColorContents;
Box overlap = info.getBounds().getIntersection(_region);
float scale = VOXEL_BLOCK_SIZE / info.size;
@ -725,32 +726,92 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) {
for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++,
hermiteDestX += VoxelHermiteData::EDGE_COUNT) {
// internal edges are set to zero; border edges (when non-terminal) are set to the intersection values
hermiteDestX[0] = 0x0;
if ((x == hermiteMinX || x == hermiteMaxX) && x != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[1])) {
hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0,
((x == hermiteMinX ? overlap.minimum.x : overlap.maximum.x) - x) * EIGHT_BIT_MAXIMUM);
int alpha1 = qAlpha(color[1]);
if (alpha0 != alpha1) {
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[1]) == alpha1) {
if (x == hermiteMinX) {
int alpha = (overlap.minimum.x - x) * EIGHT_BIT_MAXIMUM;
if (alpha <= qAlpha(hermiteDestX[0])) {
hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0, alpha);
}
} else {
int alpha = (overlap.maximum.x - x) * EIGHT_BIT_MAXIMUM;
if (alpha >= qAlpha(hermiteDestX[0])) {
hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0, alpha);
}
}
} else {
hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0,
((x == hermiteMinX ? overlap.minimum.x : overlap.maximum.x) - x) * EIGHT_BIT_MAXIMUM);
}
} else {
hermiteDestX[0] = 0x0;
}
} else {
hermiteDestX[0] = 0x0;
}
hermiteDestX[1] = 0x0;
if ((y == hermiteMinY || y == hermiteMaxY) && y != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) {
hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0,
((y == hermiteMinY ? overlap.minimum.y : overlap.maximum.y) - y) * EIGHT_BIT_MAXIMUM);
int alpha2 = qAlpha(color[VOXEL_BLOCK_SAMPLES]);
if (alpha0 != alpha2) {
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_SAMPLES]) == alpha2) {
if (y == hermiteMinY) {
int alpha = (overlap.minimum.y - y) * EIGHT_BIT_MAXIMUM;
if (alpha <= qAlpha(hermiteDestX[1])) {
hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, alpha);
}
} else {
int alpha = (overlap.maximum.y - y) * EIGHT_BIT_MAXIMUM;
if (alpha >= qAlpha(hermiteDestX[1])) {
hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, alpha);
}
}
} else {
hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0,
((y == hermiteMinY ? overlap.minimum.y : overlap.maximum.y) - y) * EIGHT_BIT_MAXIMUM);
}
} else {
hermiteDestX[1] = 0x0;
}
} else {
hermiteDestX[1] = 0x0;
}
hermiteDestX[2] = 0x0;
if ((z == hermiteMinZ || z == hermiteMaxZ) && z != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) {
hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX,
((z == hermiteMinZ ? overlap.minimum.z : overlap.maximum.z) - z) * EIGHT_BIT_MAXIMUM);
int alpha4 = qAlpha(color[VOXEL_BLOCK_AREA]);
if (alpha0 != alpha4) {
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_AREA]) == alpha4) {
if (z == hermiteMinZ) {
int alpha = (overlap.minimum.z - z) * EIGHT_BIT_MAXIMUM;
if (alpha <= qAlpha(hermiteDestX[2])) {
hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, alpha);
}
} else {
int alpha = (overlap.maximum.z - z) * EIGHT_BIT_MAXIMUM;
if (alpha >= qAlpha(hermiteDestX[2])) {
hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, alpha);
}
}
} else {
hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX,
((z == hermiteMinZ ? overlap.minimum.z : overlap.maximum.z) - z) * EIGHT_BIT_MAXIMUM);
}
} else {
hermiteDestX[2] = 0x0;
}
} else {
hermiteDestX[2] = 0x0;
}
}
}
@ -864,8 +925,9 @@ int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
QVector<QRgb> colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
QVector<QRgb> oldColorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
colorPointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME);
QVector<QRgb> colorContents = oldColorContents;
Box overlap = info.getBounds().getIntersection(_bounds);
float scale = VOXEL_BLOCK_SIZE / info.size;
@ -883,6 +945,7 @@ int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) {
float relativeRadiusSquared = relativeRadius * relativeRadius;
QRgb rgb = _color.rgba();
bool flipped = (qAlpha(rgb) == 0);
glm::vec3 position(0.0f, 0.0f, minZ);
for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z++) {
@ -932,78 +995,117 @@ int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) {
hermiteDestX += VoxelHermiteData::EDGE_COUNT) {
// at each intersected non-terminal edge, we check for a transition and, if one is detected, we assign the
// crossing and normal values based on intersection with the sphere
hermiteDestX[0] = 0x0;
glm::vec3 offset(x - relativeCenter.x, y - relativeCenter.y, z - relativeCenter.z);
glm::vec3 vector(x - relativeCenter.x, y - relativeCenter.y, z - relativeCenter.z);
if (x != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[1])) {
float radicand = relativeRadiusSquared - offset.y * offset.y - offset.z * offset.z;
int alpha1 = qAlpha(color[1]);
if (alpha0 != alpha1) {
float radicand = relativeRadiusSquared - vector.y * vector.y - vector.z * vector.z;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.x - root;
parameter = -vector.x - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.x + root, 0.0f, 1.0f);
parameter = glm::clamp(-vector.x + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
glm::vec3 normal = vector + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(0.0f, 1.0f, 0.0f);
}
hermiteDestX[0] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[1]) == alpha1) {
int alpha = parameter * EIGHT_BIT_MAXIMUM;
if (normal.x < 0.0f ? alpha <= qAlpha(hermiteDestX[0]) : alpha >= qAlpha(hermiteDestX[0])) {
hermiteDestX[0] = packNormal(flipped ? -normal : normal, alpha);
}
} else {
hermiteDestX[0] = packNormal(flipped ? -normal : normal, parameter * EIGHT_BIT_MAXIMUM);
}
} else {
hermiteDestX[0] = 0x0;
}
} else {
hermiteDestX[0] = 0x0;
}
hermiteDestX[1] = 0x0;
if (y != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) {
float radicand = relativeRadiusSquared - offset.x * offset.x - offset.z * offset.z;
int alpha2 = qAlpha(color[VOXEL_BLOCK_SAMPLES]);
if (alpha0 != alpha2) {
float radicand = relativeRadiusSquared - vector.x * vector.x - vector.z * vector.z;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.y - root;
parameter = -vector.y - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.y + root, 0.0f, 1.0f);
parameter = glm::clamp(-vector.y + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
glm::vec3 normal = vector + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(1.0f, 0.0f, 0.0f);
}
hermiteDestX[1] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_SAMPLES]) == alpha2) {
int alpha = parameter * EIGHT_BIT_MAXIMUM;
if (normal.y < 0.0f ? alpha <= qAlpha(hermiteDestX[1]) : alpha >= qAlpha(hermiteDestX[1])) {
hermiteDestX[1] = packNormal(flipped ? -normal : normal, alpha);
}
} else {
hermiteDestX[1] = packNormal(flipped ? -normal : normal, parameter * EIGHT_BIT_MAXIMUM);
}
} else {
hermiteDestX[1] = 0x0;
}
} else {
hermiteDestX[1] = 0x0;
}
hermiteDestX[2] = 0x0;
if (z != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) {
float radicand = relativeRadiusSquared - offset.x * offset.x - offset.y * offset.y;
int alpha4 = qAlpha(color[VOXEL_BLOCK_AREA]);
if (alpha0 != alpha4) {
float radicand = relativeRadiusSquared - vector.x * vector.x - vector.y * vector.y;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.z - root;
parameter = -vector.z - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.z + root, 0.0f, 1.0f);
parameter = glm::clamp(-vector.z + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
glm::vec3 normal = vector + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(1.0f, 0.0f, 0.0f);
}
hermiteDestX[2] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_AREA]) == alpha4) {
int alpha = parameter * EIGHT_BIT_MAXIMUM;
if (normal.z < 0.0f ? alpha <= qAlpha(hermiteDestX[2]) : alpha >= qAlpha(hermiteDestX[2])) {
hermiteDestX[2] = packNormal(flipped ? -normal : normal, alpha);
}
} else {
hermiteDestX[2] = packNormal(flipped ? -normal : normal, parameter * EIGHT_BIT_MAXIMUM);
}
} else {
hermiteDestX[2] = 0x0;
}
} else {
hermiteDestX[2] = 0x0;
}
}
}