mirror of
https://github.com/overte-org/overte.git
synced 2025-04-07 23:53:54 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into android
This commit is contained in:
commit
b4d6a44fc2
26 changed files with 3603 additions and 4120 deletions
|
@ -311,7 +311,7 @@ MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) :
|
|||
const char* SAVE_FILE = "/resources/metavoxels.dat";
|
||||
|
||||
const int FILE_MAGIC = 0xDADAFACE;
|
||||
const int FILE_VERSION = 2;
|
||||
const int FILE_VERSION = 4;
|
||||
|
||||
void MetavoxelPersister::load() {
|
||||
QString path = QCoreApplication::applicationDirPath() + SAVE_FILE;
|
||||
|
|
|
@ -22,7 +22,8 @@ varying vec4 alphaValues;
|
|||
|
||||
void main(void) {
|
||||
// blend the splat textures
|
||||
gl_FragColor = gl_Color * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
|
||||
gl_FragColor = vec4(gl_Color.rgb, step(1.0, gl_Color.a + 1.0 / 512.0)) *
|
||||
(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);
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// the splat texture offset
|
||||
uniform vec3 splatTextureOffset;
|
||||
|
||||
// the splat textures scales on the S axis
|
||||
uniform vec4 splatTextureScalesS;
|
||||
|
||||
|
@ -43,7 +46,7 @@ void main(void) {
|
|||
normal = gl_Normal;
|
||||
|
||||
// pass along the scaled/offset texture coordinates
|
||||
vec4 textureSpacePosition = gl_Vertex.xyyz;
|
||||
vec4 textureSpacePosition = (gl_Vertex.xyz + splatTextureOffset).xyyz;
|
||||
gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0],
|
||||
splatTextureScalesS[0], splatTextureScalesT[0]);
|
||||
gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1],
|
||||
|
|
|
@ -419,9 +419,8 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
|
||||
|
||||
QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels");
|
||||
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false,
|
||||
Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData()));
|
||||
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderSpanners, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderHeightfields, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderDualContourSurfaces, 0, true);
|
||||
addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this,
|
||||
SLOT(showMetavoxelNetworkSimulator()));
|
||||
|
|
|
@ -424,9 +424,9 @@ namespace MenuOption {
|
|||
const QString RenderDualContourSurfaces = "Render Dual Contour Surfaces";
|
||||
const QString RenderFocusIndicator = "Show Eye Focus";
|
||||
const QString RenderHeadCollisionShapes = "Show Head Collision Shapes";
|
||||
const QString RenderHeightfields = "Render Heightfields";
|
||||
const QString RenderLookAtVectors = "Show Look-at Vectors";
|
||||
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
|
||||
const QString RenderSpanners = "Render Spanners";
|
||||
const QString RenderTargetFramerate = "Framerate";
|
||||
const QString RenderTargetFramerateUnlimited = "Unlimited";
|
||||
const QString RenderTargetFramerate60 = "60";
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -26,8 +26,8 @@
|
|||
class HeightfieldBaseLayerBatch;
|
||||
class HeightfieldSplatBatch;
|
||||
class HermiteBatch;
|
||||
class MetavoxelBatch;
|
||||
class Model;
|
||||
class VoxelBatch;
|
||||
class VoxelSplatBatch;
|
||||
|
||||
/// Renders a metavoxel tree.
|
||||
|
@ -59,48 +59,35 @@ public:
|
|||
void setNetworkSimulation(const NetworkSimulation& simulation);
|
||||
NetworkSimulation getNetworkSimulation();
|
||||
|
||||
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
|
||||
const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; }
|
||||
|
||||
void simulate(float deltaTime);
|
||||
void render();
|
||||
|
||||
void renderHeightfieldCursor(const glm::vec3& position, float radius);
|
||||
|
||||
void renderVoxelCursor(const glm::vec3& position, float radius);
|
||||
|
||||
bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
|
||||
|
||||
Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color);
|
||||
|
||||
Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material);
|
||||
|
||||
Q_INVOKABLE void paintVoxelColor(const glm::vec3& position, float radius, const QColor& color);
|
||||
|
||||
Q_INVOKABLE void paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material);
|
||||
Q_INVOKABLE void setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color, bool paint = false);
|
||||
|
||||
Q_INVOKABLE void setVoxelColor(const SharedObjectPointer& spanner, const QColor& color);
|
||||
|
||||
Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material);
|
||||
Q_INVOKABLE void setHeightfieldMaterial(const SharedObjectPointer& spanner,
|
||||
const SharedObjectPointer& material, bool paint = false);
|
||||
|
||||
void addHeightfieldBaseBatch(const HeightfieldBaseLayerBatch& batch) { _heightfieldBaseBatches.append(batch); }
|
||||
void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); }
|
||||
|
||||
void addVoxelBaseBatch(const VoxelBatch& batch) { _voxelBaseBatches.append(batch); }
|
||||
void addVoxelBaseBatch(const MetavoxelBatch& batch) { _voxelBaseBatches.append(batch); }
|
||||
void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); }
|
||||
|
||||
void addHermiteBatch(const HermiteBatch& batch) { _hermiteBatches.append(batch); }
|
||||
|
||||
Q_INVOKABLE void deleteTextures(int heightTextureID, int colorTextureID, int materialTextureID) const;
|
||||
|
||||
Q_INVOKABLE void deleteBuffers(int vertexBufferID, int indexBufferID, int hermiteBufferID) const;
|
||||
|
||||
signals:
|
||||
|
||||
void rendering();
|
||||
|
||||
public slots:
|
||||
|
||||
void refreshVoxelData();
|
||||
|
||||
protected:
|
||||
|
||||
Q_INVOKABLE void applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable = false);
|
||||
|
@ -111,9 +98,6 @@ private:
|
|||
|
||||
void guideToAugmented(MetavoxelVisitor& visitor, bool render = false);
|
||||
|
||||
AttributePointer _heightfieldBufferAttribute;
|
||||
AttributePointer _voxelBufferAttribute;
|
||||
|
||||
MetavoxelLOD _lod;
|
||||
QReadWriteLock _lodLock;
|
||||
Frustum _frustum;
|
||||
|
@ -123,7 +107,7 @@ private:
|
|||
|
||||
QVector<HeightfieldBaseLayerBatch> _heightfieldBaseBatches;
|
||||
QVector<HeightfieldSplatBatch> _heightfieldSplatBatches;
|
||||
QVector<VoxelBatch> _voxelBaseBatches;
|
||||
QVector<MetavoxelBatch> _voxelBaseBatches;
|
||||
QVector<VoxelSplatBatch> _voxelSplatBatches;
|
||||
QVector<HermiteBatch> _hermiteBatches;
|
||||
|
||||
|
@ -166,16 +150,21 @@ private:
|
|||
static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations);
|
||||
};
|
||||
|
||||
/// Base class for heightfield batches.
|
||||
class HeightfieldBatch {
|
||||
/// Base class for all batches.
|
||||
class MetavoxelBatch {
|
||||
public:
|
||||
QOpenGLBuffer* vertexBuffer;
|
||||
QOpenGLBuffer* indexBuffer;
|
||||
GLuint vertexBufferID;
|
||||
GLuint indexBufferID;
|
||||
glm::vec3 translation;
|
||||
glm::quat rotation;
|
||||
glm::vec3 scale;
|
||||
int vertexCount;
|
||||
int indexCount;
|
||||
};
|
||||
|
||||
/// Base class for heightfield batches.
|
||||
class HeightfieldBatch : public MetavoxelBatch {
|
||||
public:
|
||||
GLuint heightTextureID;
|
||||
glm::vec4 heightScale;
|
||||
};
|
||||
|
@ -199,18 +188,10 @@ public:
|
|||
int materialIndex;
|
||||
};
|
||||
|
||||
/// Base class for voxel batches.
|
||||
class VoxelBatch {
|
||||
public:
|
||||
QOpenGLBuffer* vertexBuffer;
|
||||
QOpenGLBuffer* indexBuffer;
|
||||
int vertexCount;
|
||||
int indexCount;
|
||||
};
|
||||
|
||||
/// A batch containing a voxel splat.
|
||||
class VoxelSplatBatch : public VoxelBatch {
|
||||
class VoxelSplatBatch : public MetavoxelBatch {
|
||||
public:
|
||||
glm::vec3 splatTextureOffset;
|
||||
int splatTextureIDs[4];
|
||||
glm::vec4 splatTextureScalesS;
|
||||
glm::vec4 splatTextureScalesT;
|
||||
|
@ -220,7 +201,10 @@ public:
|
|||
/// A batch containing Hermite data for debugging.
|
||||
class HermiteBatch {
|
||||
public:
|
||||
QOpenGLBuffer* vertexBuffer;
|
||||
GLuint vertexBufferID;
|
||||
glm::vec3 translation;
|
||||
glm::quat rotation;
|
||||
glm::vec3 scale;
|
||||
int vertexCount;
|
||||
};
|
||||
|
||||
|
@ -271,8 +255,6 @@ public:
|
|||
void setRenderedAugmentedData(const MetavoxelData& data) { _renderedAugmentedData = data; }
|
||||
|
||||
virtual int parseData(const QByteArray& packet);
|
||||
|
||||
Q_INVOKABLE void refreshVoxelData();
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -295,7 +277,8 @@ public:
|
|||
|
||||
virtual ~BufferData();
|
||||
|
||||
virtual void render(bool cursor = false) = 0;
|
||||
virtual void render(const glm::vec3& translation, const glm::quat& rotation,
|
||||
const glm::vec3& scale, bool cursor = false) = 0;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer;
|
||||
|
@ -334,44 +317,34 @@ public:
|
|||
VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
|
||||
const QMultiHash<VoxelCoord, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
|
||||
QVector<SharedObjectPointer>());
|
||||
virtual ~VoxelBuffer();
|
||||
|
||||
bool isHermiteEnabled() const { return _hermiteEnabled; }
|
||||
|
||||
/// Finds the first intersection between the described ray and the voxel data.
|
||||
/// \param entry the entry point of the ray in relative coordinates, from (0, 0, 0) to (1, 1, 1)
|
||||
bool findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance) const;
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
|
||||
|
||||
virtual void render(bool cursor = false);
|
||||
virtual void render(const glm::vec3& translation, const glm::quat& rotation,
|
||||
const glm::vec3& scale, bool cursor = false);
|
||||
|
||||
private:
|
||||
|
||||
QVector<VoxelPoint> _vertices;
|
||||
QVector<int> _indices;
|
||||
QVector<glm::vec3> _hermite;
|
||||
bool _hermiteEnabled;
|
||||
QMultiHash<VoxelCoord, int> _quadIndices;
|
||||
int _size;
|
||||
int _vertexCount;
|
||||
int _indexCount;
|
||||
int _hermiteCount;
|
||||
QOpenGLBuffer _vertexBuffer;
|
||||
QOpenGLBuffer _indexBuffer;
|
||||
QOpenGLBuffer _hermiteBuffer;
|
||||
GLuint _vertexBufferID;
|
||||
GLuint _indexBufferID;
|
||||
GLuint _hermiteBufferID;
|
||||
QVector<SharedObjectPointer> _materials;
|
||||
QVector<NetworkTexturePointer> _networkTextures;
|
||||
};
|
||||
|
||||
/// A client-side attribute that stores renderable buffers.
|
||||
class BufferDataAttribute : public InlineAttribute<BufferDataPointer> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE BufferDataAttribute(const QString& name = QString());
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
|
||||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||
};
|
||||
|
||||
/// Renders metavoxels as points.
|
||||
class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplementation {
|
||||
Q_OBJECT
|
||||
|
@ -380,7 +353,6 @@ public:
|
|||
|
||||
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
||||
|
||||
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
||||
virtual void simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
||||
virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
||||
};
|
||||
|
@ -450,6 +422,9 @@ public:
|
|||
HeightfieldNodeRenderer();
|
||||
virtual ~HeightfieldNodeRenderer();
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
|
||||
|
||||
void render(const HeightfieldNodePointer& node, const glm::vec3& translation,
|
||||
const glm::quat& rotation, const glm::vec3& scale, bool cursor);
|
||||
|
||||
|
@ -460,6 +435,8 @@ private:
|
|||
GLuint _materialTextureID;
|
||||
QVector<NetworkTexturePointer> _networkTextures;
|
||||
|
||||
BufferDataPointer _voxels;
|
||||
|
||||
typedef QPair<int, int> IntPair;
|
||||
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
||||
static QHash<IntPair, BufferPair> _bufferPairs;
|
||||
|
|
|
@ -97,7 +97,7 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
_gridSpacing->setMinimum(-FLT_MAX);
|
||||
_gridSpacing->setMaximum(FLT_MAX);
|
||||
_gridSpacing->setPrefix("2^");
|
||||
_gridSpacing->setValue(-3.0);
|
||||
_gridSpacing->setValue(0.0);
|
||||
connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(alignGridPosition()));
|
||||
|
||||
formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox());
|
||||
|
@ -125,13 +125,13 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
addTool(new InsertSpannerTool(this));
|
||||
addTool(new RemoveSpannerTool(this));
|
||||
addTool(new ClearSpannersTool(this));
|
||||
addTool(new ImportHeightfieldTool(this));
|
||||
addTool(new HeightfieldHeightBrushTool(this));
|
||||
addTool(new HeightfieldMaterialBrushTool(this));
|
||||
addTool(new ImportHeightfieldTool(this));
|
||||
addTool(new VoxelMaterialBoxTool(this));
|
||||
addTool(new VoxelMaterialSpannerTool(this));
|
||||
addTool(new VoxelMaterialBrushTool(this));
|
||||
addTool(new VoxelSculptBrushTool(this));
|
||||
addTool(new HeightfieldSculptBrushTool(this));
|
||||
addTool(new HeightfieldFillBrushTool(this));
|
||||
addTool(new HeightfieldMaterialBoxTool(this));
|
||||
addTool(new HeightfieldMaterialSpannerTool(this));
|
||||
|
||||
updateAttributes();
|
||||
|
||||
|
@ -331,6 +331,9 @@ void MetavoxelEditor::render() {
|
|||
MetavoxelTool* tool = getActiveTool();
|
||||
if (tool) {
|
||||
tool->render();
|
||||
if (!tool->getUsesGrid()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
@ -386,10 +389,11 @@ MetavoxelTool* MetavoxelEditor::getActiveTool() const {
|
|||
|
||||
ProgramObject MetavoxelEditor::_gridProgram;
|
||||
|
||||
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :
|
||||
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing, bool usesGrid) :
|
||||
_editor(editor),
|
||||
_usesValue(usesValue),
|
||||
_userFacing(userFacing) {
|
||||
_userFacing(userFacing),
|
||||
_usesGrid(usesGrid) {
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
setLayout(layout);
|
||||
|
@ -670,7 +674,7 @@ void InsertSpannerTool::applyEdit(const AttributePointer& attribute, const Share
|
|||
}
|
||||
|
||||
RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) :
|
||||
MetavoxelTool(editor, "Remove Spanner", false) {
|
||||
MetavoxelTool(editor, "Remove Spanner", false, true, false) {
|
||||
}
|
||||
|
||||
bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const {
|
||||
|
@ -697,7 +701,7 @@ bool RemoveSpannerTool::eventFilter(QObject* watched, QEvent* event) {
|
|||
}
|
||||
|
||||
ClearSpannersTool::ClearSpannersTool(MetavoxelEditor* editor) :
|
||||
MetavoxelTool(editor, "Clear Spanners", false) {
|
||||
MetavoxelTool(editor, "Clear Spanners", false, true, false) {
|
||||
|
||||
QPushButton* button = new QPushButton("Clear");
|
||||
layout()->addWidget(button);
|
||||
|
@ -718,7 +722,7 @@ void ClearSpannersTool::clear() {
|
|||
}
|
||||
|
||||
HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) :
|
||||
MetavoxelTool(editor, name, false) {
|
||||
MetavoxelTool(editor, name, false, true, false) {
|
||||
|
||||
QWidget* widget = new QWidget();
|
||||
widget->setLayout(_form = new QFormLayout());
|
||||
|
@ -807,7 +811,7 @@ void ImportHeightfieldTool::updateSpanner() {
|
|||
}
|
||||
|
||||
HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) :
|
||||
MetavoxelTool(editor, name, false),
|
||||
MetavoxelTool(editor, name, false, true, false),
|
||||
_positionValid(false) {
|
||||
|
||||
QWidget* widget = new QWidget();
|
||||
|
@ -817,7 +821,7 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin
|
|||
_form->addRow("Radius:", _radius = new QDoubleSpinBox());
|
||||
_radius->setSingleStep(0.01);
|
||||
_radius->setMaximum(FLT_MAX);
|
||||
_radius->setValue(1.0);
|
||||
_radius->setValue(5.0);
|
||||
}
|
||||
|
||||
bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const {
|
||||
|
@ -865,11 +869,19 @@ HeightfieldHeightBrushTool::HeightfieldHeightBrushTool(MetavoxelEditor* editor)
|
|||
_height->setMinimum(-FLT_MAX);
|
||||
_height->setMaximum(FLT_MAX);
|
||||
_height->setValue(1.0);
|
||||
|
||||
_form->addRow("Mode:", _mode = new QComboBox());
|
||||
_mode->addItem("Raise/Lower");
|
||||
_mode->addItem("Set");
|
||||
_mode->addItem("Erase");
|
||||
}
|
||||
|
||||
QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) {
|
||||
const int SET_MODE_INDEX = 1;
|
||||
const int ERASE_MODE_INDEX = 2;
|
||||
return QVariant::fromValue(PaintHeightfieldHeightEdit(_position, _radius->value(),
|
||||
alternate ? -_height->value() : _height->value()));
|
||||
alternate ? -_height->value() : _height->value(), _mode->currentIndex() == SET_MODE_INDEX,
|
||||
_mode->currentIndex() == ERASE_MODE_INDEX));
|
||||
}
|
||||
|
||||
MaterialControl::MaterialControl(QWidget* widget, QFormLayout* form, bool clearable) :
|
||||
|
@ -939,16 +951,58 @@ HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* edit
|
|||
}
|
||||
|
||||
QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) {
|
||||
Sphere* sphere = new Sphere();
|
||||
sphere->setTranslation(_position);
|
||||
sphere->setScale(_radius->value());
|
||||
if (alternate) {
|
||||
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
|
||||
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
|
||||
SharedObjectPointer(), QColor(0, 0, 0, 0), true));
|
||||
} else {
|
||||
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), _materialControl->getMaterial(),
|
||||
_materialControl->getColor()));
|
||||
}
|
||||
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
|
||||
_materialControl->getMaterial(), _materialControl->getColor(), true));
|
||||
}
|
||||
}
|
||||
|
||||
VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
|
||||
BoxTool(editor, "Set Voxel Material (Box)", false) {
|
||||
HeightfieldSculptBrushTool::HeightfieldSculptBrushTool(MetavoxelEditor* editor) :
|
||||
HeightfieldBrushTool(editor, "Sculpt Brush"),
|
||||
_materialControl(new MaterialControl(this, _form, true)) {
|
||||
}
|
||||
|
||||
QVariant HeightfieldSculptBrushTool::createEdit(bool alternate) {
|
||||
Sphere* sphere = new Sphere();
|
||||
sphere->setTranslation(_position);
|
||||
sphere->setScale(_radius->value());
|
||||
if (alternate) {
|
||||
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
|
||||
SharedObjectPointer(), QColor(0, 0, 0, 0)));
|
||||
} else {
|
||||
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
|
||||
_materialControl->getMaterial(), _materialControl->getColor()));
|
||||
}
|
||||
}
|
||||
|
||||
HeightfieldFillBrushTool::HeightfieldFillBrushTool(MetavoxelEditor* editor) :
|
||||
HeightfieldBrushTool(editor, "Fill Brush") {
|
||||
|
||||
_form->addRow("Mode:", _mode = new QComboBox());
|
||||
_mode->addItem("Fill");
|
||||
_mode->addItem("Voxelize");
|
||||
}
|
||||
|
||||
QVariant HeightfieldFillBrushTool::createEdit(bool alternate) {
|
||||
const int FILL_MODE_INDEX = 0;
|
||||
if (_mode->currentIndex() == FILL_MODE_INDEX) {
|
||||
return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value()));
|
||||
}
|
||||
Sphere* sphere = new Sphere();
|
||||
sphere->setTranslation(_position);
|
||||
sphere->setScale(_radius->value());
|
||||
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
|
||||
SharedObjectPointer(), QColor(), false, true));
|
||||
}
|
||||
|
||||
HeightfieldMaterialBoxTool::HeightfieldMaterialBoxTool(MetavoxelEditor* editor) :
|
||||
BoxTool(editor, "Set Material (Box)", false) {
|
||||
|
||||
QWidget* widget = new QWidget();
|
||||
QFormLayout* form = new QFormLayout();
|
||||
|
@ -965,32 +1019,32 @@ VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
|
|||
_materialControl = new MaterialControl(this, form, true);
|
||||
}
|
||||
|
||||
bool VoxelMaterialBoxTool::appliesTo(const AttributePointer& attribute) const {
|
||||
return attribute->inherits("VoxelColorAttribute");
|
||||
bool HeightfieldMaterialBoxTool::appliesTo(const AttributePointer& attribute) const {
|
||||
return attribute->inherits("SpannerSetAttribute");
|
||||
}
|
||||
|
||||
bool VoxelMaterialBoxTool::shouldSnapToGrid() {
|
||||
bool HeightfieldMaterialBoxTool::shouldSnapToGrid() {
|
||||
return _snapToGrid->isChecked();
|
||||
}
|
||||
|
||||
QColor VoxelMaterialBoxTool::getColor() {
|
||||
QColor HeightfieldMaterialBoxTool::getColor() {
|
||||
return _materialControl->getColor();
|
||||
}
|
||||
|
||||
void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
|
||||
void HeightfieldMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
|
||||
Cuboid* cuboid = new Cuboid();
|
||||
cuboid->setTranslation((maximum + minimum) * 0.5f);
|
||||
glm::vec3 vector = (maximum - minimum) * 0.5f;
|
||||
cuboid->setScale(vector.x);
|
||||
cuboid->setAspectY(vector.y / vector.x);
|
||||
cuboid->setAspectZ(vector.z / vector.x);
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(cuboid),
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(cuboid),
|
||||
_materialControl->getMaterial(), _materialControl->getColor())) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) :
|
||||
PlaceSpannerTool(editor, "Set Voxel Material (Spanner)", QString(), false) {
|
||||
HeightfieldMaterialSpannerTool::HeightfieldMaterialSpannerTool(MetavoxelEditor* editor) :
|
||||
PlaceSpannerTool(editor, "Set Material (Spanner)", QString(), false) {
|
||||
|
||||
QWidget* widget = new QWidget();
|
||||
QFormLayout* form = new QFormLayout();
|
||||
|
@ -1004,110 +1058,25 @@ VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) :
|
|||
|
||||
QPushButton* place = new QPushButton("Set");
|
||||
layout()->addWidget(place);
|
||||
connect(place, &QPushButton::clicked, this, &VoxelMaterialSpannerTool::place);
|
||||
connect(place, &QPushButton::clicked, this, &HeightfieldMaterialSpannerTool::place);
|
||||
}
|
||||
|
||||
bool VoxelMaterialSpannerTool::appliesTo(const AttributePointer& attribute) const {
|
||||
return attribute->inherits("VoxelColorAttribute");
|
||||
bool HeightfieldMaterialSpannerTool::appliesTo(const AttributePointer& attribute) const {
|
||||
return attribute->inherits("SpannerSetAttribute");
|
||||
}
|
||||
|
||||
SharedObjectPointer VoxelMaterialSpannerTool::getSpanner() {
|
||||
SharedObjectPointer HeightfieldMaterialSpannerTool::getSpanner() {
|
||||
return _spannerEditor->getObject();
|
||||
}
|
||||
|
||||
QColor VoxelMaterialSpannerTool::getColor() {
|
||||
QColor HeightfieldMaterialSpannerTool::getColor() {
|
||||
return _materialControl->getColor();
|
||||
}
|
||||
|
||||
void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
|
||||
void HeightfieldMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
|
||||
static_cast<Spanner*>(spanner.data())->setWillBeVoxelized(true);
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner,
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner,
|
||||
_materialControl->getMaterial(), _materialControl->getColor())) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
|
||||
VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) :
|
||||
MetavoxelTool(editor, name, false, true),
|
||||
_positionValid(false) {
|
||||
|
||||
QWidget* widget = new QWidget();
|
||||
widget->setLayout(_form = new QFormLayout());
|
||||
layout()->addWidget(widget);
|
||||
|
||||
_form->addRow("Radius:", _radius = new QDoubleSpinBox());
|
||||
_radius->setSingleStep(0.01);
|
||||
_radius->setMaximum(FLT_MAX);
|
||||
_radius->setValue(0.25);
|
||||
}
|
||||
|
||||
bool VoxelBrushTool::appliesTo(const AttributePointer& attribute) const {
|
||||
return attribute->inherits("VoxelColorAttribute");
|
||||
}
|
||||
|
||||
void VoxelBrushTool::render() {
|
||||
if (Application::getInstance()->isMouseHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find the intersection with the voxels
|
||||
glm::vec3 origin = Application::getInstance()->getMouseRayOrigin();
|
||||
glm::vec3 direction = Application::getInstance()->getMouseRayDirection();
|
||||
|
||||
float heightfieldDistance = FLT_MAX, voxelDistance = FLT_MAX;
|
||||
if (!(Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection(
|
||||
origin, direction, heightfieldDistance) |
|
||||
Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, voxelDistance))) {
|
||||
_positionValid = false;
|
||||
return;
|
||||
}
|
||||
_positionValid = true;
|
||||
Application::getInstance()->getMetavoxels()->renderVoxelCursor(
|
||||
_position = origin + qMin(heightfieldDistance, voxelDistance) * direction, _radius->value());
|
||||
}
|
||||
|
||||
bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) {
|
||||
if (event->type() == QEvent::Wheel) {
|
||||
float angle = static_cast<QWheelEvent*>(event)->angleDelta().y();
|
||||
const float ANGLE_SCALE = 1.0f / 1000.0f;
|
||||
_radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE));
|
||||
return true;
|
||||
|
||||
} else if (event->type() == QEvent::MouseButtonPress && _positionValid) {
|
||||
MetavoxelEditMessage message = { createEdit(static_cast<QMouseEvent*>(event)->button() == Qt::RightButton) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
VoxelMaterialBrushTool::VoxelMaterialBrushTool(MetavoxelEditor* editor) :
|
||||
VoxelBrushTool(editor, "Material Brush"),
|
||||
_materialControl(new MaterialControl(this, _form)) {
|
||||
}
|
||||
|
||||
QVariant VoxelMaterialBrushTool::createEdit(bool alternate) {
|
||||
if (alternate) {
|
||||
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
|
||||
} else {
|
||||
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(),
|
||||
_materialControl->getMaterial(), _materialControl->getColor()));
|
||||
}
|
||||
}
|
||||
|
||||
VoxelSculptBrushTool::VoxelSculptBrushTool(MetavoxelEditor* editor) :
|
||||
VoxelBrushTool(editor, "Sculpt Brush"),
|
||||
_materialControl(new MaterialControl(this, _form, true)) {
|
||||
}
|
||||
|
||||
QVariant VoxelSculptBrushTool::createEdit(bool alternate) {
|
||||
Sphere* sphere = new Sphere();
|
||||
sphere->setTranslation(_position);
|
||||
sphere->setScale(_radius->value());
|
||||
if (alternate) {
|
||||
return QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(sphere),
|
||||
SharedObjectPointer(), QColor(0, 0, 0, 0)));
|
||||
} else {
|
||||
return QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(sphere),
|
||||
_materialControl->getMaterial(), _materialControl->getColor()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,12 +92,15 @@ class MetavoxelTool : public QWidget {
|
|||
|
||||
public:
|
||||
|
||||
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true);
|
||||
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true,
|
||||
bool userFacing = true, bool usesGrid = true);
|
||||
|
||||
bool getUsesValue() const { return _usesValue; }
|
||||
|
||||
bool isUserFacing() const { return _userFacing; }
|
||||
|
||||
bool getUsesGrid() const { return _usesGrid; }
|
||||
|
||||
virtual bool appliesTo(const AttributePointer& attribute) const;
|
||||
|
||||
virtual void simulate(float deltaTime);
|
||||
|
@ -113,6 +116,7 @@ protected:
|
|||
MetavoxelEditor* _editor;
|
||||
bool _usesValue;
|
||||
bool _userFacing;
|
||||
bool _usesGrid;
|
||||
};
|
||||
|
||||
/// Base class for tools that allow dragging out a 3D box.
|
||||
|
@ -342,6 +346,7 @@ protected:
|
|||
private:
|
||||
|
||||
QDoubleSpinBox* _height;
|
||||
QComboBox* _mode;
|
||||
};
|
||||
|
||||
/// Contains widgets for editing materials.
|
||||
|
@ -387,13 +392,47 @@ private:
|
|||
MaterialControl* _materialControl;
|
||||
};
|
||||
|
||||
/// Allows setting voxel materials by dragging out a box.
|
||||
class VoxelMaterialBoxTool : public BoxTool {
|
||||
/// Allows sculpting parts of the heightfield.
|
||||
class HeightfieldSculptBrushTool : public HeightfieldBrushTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoxelMaterialBoxTool(MetavoxelEditor* editor);
|
||||
HeightfieldSculptBrushTool(MetavoxelEditor* editor);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(bool alternate);
|
||||
|
||||
private:
|
||||
|
||||
MaterialControl* _materialControl;
|
||||
};
|
||||
|
||||
/// Allows "filling" (removing dual contour stack data) parts of the heightfield.
|
||||
class HeightfieldFillBrushTool : public HeightfieldBrushTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
HeightfieldFillBrushTool(MetavoxelEditor* editor);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(bool alternate);
|
||||
|
||||
private:
|
||||
|
||||
QComboBox* _mode;
|
||||
};
|
||||
|
||||
/// Allows setting heightfield materials by dragging out a box.
|
||||
class HeightfieldMaterialBoxTool : public BoxTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
HeightfieldMaterialBoxTool(MetavoxelEditor* editor);
|
||||
|
||||
virtual bool appliesTo(const AttributePointer& attribute) const;
|
||||
|
||||
|
@ -411,13 +450,13 @@ private:
|
|||
MaterialControl* _materialControl;
|
||||
};
|
||||
|
||||
/// Allows setting voxel materials by placing a spanner.
|
||||
class VoxelMaterialSpannerTool : public PlaceSpannerTool {
|
||||
/// Allows setting heightfield materials by placing a spanner.
|
||||
class HeightfieldMaterialSpannerTool : public PlaceSpannerTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoxelMaterialSpannerTool(MetavoxelEditor* editor);
|
||||
HeightfieldMaterialSpannerTool(MetavoxelEditor* editor);
|
||||
|
||||
virtual bool appliesTo(const AttributePointer& attribute) const;
|
||||
|
||||
|
@ -433,63 +472,4 @@ private:
|
|||
MaterialControl* _materialControl;
|
||||
};
|
||||
|
||||
/// Base class for voxel brush tools.
|
||||
class VoxelBrushTool : public MetavoxelTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoxelBrushTool(MetavoxelEditor* editor, const QString& name);
|
||||
|
||||
virtual bool appliesTo(const AttributePointer& attribute) const;
|
||||
|
||||
virtual void render();
|
||||
|
||||
virtual bool eventFilter(QObject* watched, QEvent* event);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(bool alternate) = 0;
|
||||
|
||||
QFormLayout* _form;
|
||||
QDoubleSpinBox* _radius;
|
||||
|
||||
glm::vec3 _position;
|
||||
bool _positionValid;
|
||||
};
|
||||
|
||||
/// Allows texturing parts of the voxel field.
|
||||
class VoxelMaterialBrushTool : public VoxelBrushTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoxelMaterialBrushTool(MetavoxelEditor* editor);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(bool alternate);
|
||||
|
||||
private:
|
||||
|
||||
MaterialControl* _materialControl;
|
||||
};
|
||||
|
||||
/// Allows sculpting parts of the voxel field.
|
||||
class VoxelSculptBrushTool : public VoxelBrushTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoxelSculptBrushTool(MetavoxelEditor* editor);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(bool alternate);
|
||||
|
||||
private:
|
||||
|
||||
MaterialControl* _materialControl;
|
||||
};
|
||||
|
||||
#endif // hifi_MetavoxelEditor_h
|
||||
|
|
|
@ -20,13 +20,9 @@
|
|||
#include "Spanner.h"
|
||||
|
||||
REGISTER_META_OBJECT(FloatAttribute)
|
||||
REGISTER_META_OBJECT(MaterialObject)
|
||||
REGISTER_META_OBJECT(SharedObjectAttribute)
|
||||
REGISTER_META_OBJECT(SharedObjectSetAttribute)
|
||||
REGISTER_META_OBJECT(SpannerSetAttribute)
|
||||
REGISTER_META_OBJECT(VoxelColorAttribute)
|
||||
REGISTER_META_OBJECT(VoxelHermiteAttribute)
|
||||
REGISTER_META_OBJECT(VoxelMaterialAttribute)
|
||||
|
||||
static int attributePointerMetaTypeId = qRegisterMetaType<AttributePointer>();
|
||||
static int ownedAttributeValueMetaTypeId = qRegisterMetaType<OwnedAttributeValue>();
|
||||
|
@ -41,21 +37,12 @@ AttributeRegistry::AttributeRegistry() :
|
|||
new DefaultMetavoxelGuide()))),
|
||||
_rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject,
|
||||
new DefaultMetavoxelRenderer()))),
|
||||
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))),
|
||||
_voxelColorAttribute(registerAttribute(new VoxelColorAttribute("voxelColor"))),
|
||||
_voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))),
|
||||
_voxelHermiteAttribute(registerAttribute(new VoxelHermiteAttribute("voxelHermite"))) {
|
||||
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))) {
|
||||
|
||||
// our baseline LOD threshold is for voxels; spanners and heightfields are a different story
|
||||
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f;
|
||||
// our baseline LOD threshold is for voxels; spanners are a different story
|
||||
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 16.0f;
|
||||
_spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER);
|
||||
_spannersAttribute->setUserFacing(true);
|
||||
|
||||
const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 16.0f;
|
||||
_voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
|
||||
_voxelColorAttribute->setUserFacing(true);
|
||||
_voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
|
||||
_voxelHermiteAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
|
||||
}
|
||||
|
||||
static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) {
|
||||
|
@ -275,846 +262,6 @@ FloatAttribute::FloatAttribute(const QString& name) :
|
|||
SimpleInlineAttribute(name) {
|
||||
}
|
||||
|
||||
const float CHAR_SCALE = 127.0f;
|
||||
const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE;
|
||||
|
||||
QRgb packNormal(const glm::vec3& normal) {
|
||||
return qRgb((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE));
|
||||
}
|
||||
|
||||
QRgb packNormal(const glm::vec3& normal, int alpha) {
|
||||
return qRgba((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE), alpha);
|
||||
}
|
||||
|
||||
glm::vec3 unpackNormal(QRgb value) {
|
||||
return glm::vec3((char)qRed(value) * INVERSE_CHAR_SCALE, (char)qGreen(value) * INVERSE_CHAR_SCALE,
|
||||
(char)qBlue(value) * INVERSE_CHAR_SCALE);
|
||||
}
|
||||
|
||||
DataBlock::~DataBlock() {
|
||||
}
|
||||
|
||||
MaterialObject::MaterialObject() :
|
||||
_scaleS(1.0f),
|
||||
_scaleT(1.0f) {
|
||||
}
|
||||
|
||||
static QHash<uchar, int> countIndices(const QByteArray& contents) {
|
||||
QHash<uchar, int> counts;
|
||||
for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) {
|
||||
if (*src != 0) {
|
||||
counts[*src]++;
|
||||
}
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
const float EIGHT_BIT_MAXIMUM = 255.0f;
|
||||
|
||||
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents) {
|
||||
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
|
||||
return 0;
|
||||
}
|
||||
// first look for a matching existing material, noting the first reusable slot
|
||||
int firstEmptyIndex = -1;
|
||||
for (int i = 0; i < materials.size(); i++) {
|
||||
const SharedObjectPointer& existingMaterial = materials.at(i);
|
||||
if (existingMaterial) {
|
||||
if (existingMaterial->equals(material.data())) {
|
||||
return i + 1;
|
||||
}
|
||||
} else if (firstEmptyIndex == -1) {
|
||||
firstEmptyIndex = i;
|
||||
}
|
||||
}
|
||||
// if nothing found, use the first empty slot or append
|
||||
if (firstEmptyIndex != -1) {
|
||||
materials[firstEmptyIndex] = material;
|
||||
return firstEmptyIndex + 1;
|
||||
}
|
||||
if (materials.size() < EIGHT_BIT_MAXIMUM) {
|
||||
materials.append(material);
|
||||
return materials.size();
|
||||
}
|
||||
// last resort: find the least-used material and remove it
|
||||
QHash<uchar, int> counts = countIndices(contents);
|
||||
uchar materialIndex = 0;
|
||||
int lowestCount = INT_MAX;
|
||||
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
|
||||
if (it.value() < lowestCount) {
|
||||
materialIndex = it.key();
|
||||
lowestCount = it.value();
|
||||
}
|
||||
}
|
||||
contents.replace((char)materialIndex, (char)0);
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents) {
|
||||
QHash<uchar, int> counts = countIndices(contents);
|
||||
for (int i = 0; i < materials.size(); i++) {
|
||||
if (counts.value(i + 1) == 0) {
|
||||
materials[i] = SharedObjectPointer();
|
||||
}
|
||||
}
|
||||
while (!(materials.isEmpty() || materials.last())) {
|
||||
materials.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6;
|
||||
|
||||
static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ,
|
||||
int sizeX, int sizeY, int sizeZ, const QVector<QRgb>& contents) {
|
||||
QByteArray inflated(VOXEL_COLOR_HEADER_SIZE, 0);
|
||||
qint32* header = (qint32*)inflated.data();
|
||||
*header++ = offsetX;
|
||||
*header++ = offsetY;
|
||||
*header++ = offsetZ;
|
||||
*header++ = sizeX;
|
||||
*header++ = sizeY;
|
||||
*header++ = sizeZ;
|
||||
inflated.append((const char*)contents.constData(), contents.size() * sizeof(QRgb));
|
||||
return qCompress(inflated);
|
||||
}
|
||||
|
||||
static QVector<QRgb> decodeVoxelColor(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ,
|
||||
int& sizeX, int& sizeY, int& sizeZ) {
|
||||
QByteArray inflated = qUncompress(encoded);
|
||||
const qint32* header = (const qint32*)inflated.constData();
|
||||
offsetX = *header++;
|
||||
offsetY = *header++;
|
||||
offsetZ = *header++;
|
||||
sizeX = *header++;
|
||||
sizeY = *header++;
|
||||
sizeZ = *header++;
|
||||
int payloadSize = inflated.size() - VOXEL_COLOR_HEADER_SIZE;
|
||||
QVector<QRgb> contents(payloadSize / sizeof(QRgb));
|
||||
memcpy(contents.data(), inflated.constData() + VOXEL_COLOR_HEADER_SIZE, payloadSize);
|
||||
return contents;
|
||||
}
|
||||
|
||||
VoxelColorData::VoxelColorData(const QVector<QRgb>& contents, int size) :
|
||||
_contents(contents),
|
||||
_size(size) {
|
||||
}
|
||||
|
||||
VoxelColorData::VoxelColorData(Bitstream& in, int bytes) {
|
||||
read(in, bytes);
|
||||
}
|
||||
|
||||
VoxelColorData::VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference) {
|
||||
if (!reference) {
|
||||
read(in, bytes);
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||
reference->setEncodedDelta(in.readAligned(bytes));
|
||||
reference->setDeltaData(DataBlockPointer(this));
|
||||
_contents = reference->getContents();
|
||||
_size = reference->getSize();
|
||||
|
||||
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
|
||||
QVector<QRgb> delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
|
||||
if (delta.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (offsetX == 0) {
|
||||
_contents = delta;
|
||||
_size = sizeX;
|
||||
return;
|
||||
}
|
||||
int minX = offsetX - 1;
|
||||
int minY = offsetY - 1;
|
||||
int minZ = offsetZ - 1;
|
||||
const QRgb* src = delta.constData();
|
||||
int size2 = _size * _size;
|
||||
QRgb* planeDest = _contents.data() + minZ * size2 + minY * _size + minX;
|
||||
int length = sizeX * sizeof(QRgb);
|
||||
for (int z = 0; z < sizeZ; z++, planeDest += size2) {
|
||||
QRgb* dest = planeDest;
|
||||
for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) {
|
||||
memcpy(dest, src, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelColorData::write(Bitstream& out) {
|
||||
QMutexLocker locker(&_encodedMutex);
|
||||
if (_encoded.isEmpty()) {
|
||||
_encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents);
|
||||
}
|
||||
out << _encoded.size();
|
||||
out.writeAligned(_encoded);
|
||||
}
|
||||
|
||||
void VoxelColorData::writeDelta(Bitstream& out, const VoxelColorDataPointer& reference) {
|
||||
if (!reference || reference->getSize() != _size) {
|
||||
write(out);
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
|
||||
int minX = _size, minY = _size, minZ = _size;
|
||||
int maxX = -1, maxY = -1, maxZ = -1;
|
||||
const QRgb* src = _contents.constData();
|
||||
const QRgb* ref = reference->getContents().constData();
|
||||
for (int z = 0; z < _size; z++) {
|
||||
bool differenceZ = false;
|
||||
for (int y = 0; y < _size; y++) {
|
||||
bool differenceY = false;
|
||||
for (int x = 0; x < _size; x++) {
|
||||
if (*src++ != *ref++) {
|
||||
minX = qMin(minX, x);
|
||||
maxX = qMax(maxX, x);
|
||||
differenceY = differenceZ = true;
|
||||
}
|
||||
}
|
||||
if (differenceY) {
|
||||
minY = qMin(minY, y);
|
||||
maxY = qMax(maxY, y);
|
||||
}
|
||||
}
|
||||
if (differenceZ) {
|
||||
minZ = qMin(minZ, z);
|
||||
maxZ = qMax(maxZ, z);
|
||||
}
|
||||
}
|
||||
QVector<QRgb> delta;
|
||||
int sizeX = 0, sizeY = 0, sizeZ = 0;
|
||||
if (maxX >= minX) {
|
||||
sizeX = maxX - minX + 1;
|
||||
sizeY = maxY - minY + 1;
|
||||
sizeZ = maxZ - minZ + 1;
|
||||
delta = QVector<QRgb>(sizeX * sizeY * sizeZ, 0);
|
||||
QRgb* dest = delta.data();
|
||||
int size2 = _size * _size;
|
||||
const QRgb* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX;
|
||||
int length = sizeX * sizeof(QRgb);
|
||||
for (int z = 0; z < sizeZ; z++, planeSrc += size2) {
|
||||
src = planeSrc;
|
||||
for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) {
|
||||
memcpy(dest, src, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
|
||||
reference->setDeltaData(DataBlockPointer(this));
|
||||
}
|
||||
out << reference->getEncodedDelta().size();
|
||||
out.writeAligned(reference->getEncodedDelta());
|
||||
}
|
||||
|
||||
void VoxelColorData::read(Bitstream& in, int bytes) {
|
||||
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
|
||||
_contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
|
||||
_size = sizeX;
|
||||
}
|
||||
|
||||
VoxelColorAttribute::VoxelColorAttribute(const QString& name) :
|
||||
InlineAttribute<VoxelColorDataPointer>(name) {
|
||||
}
|
||||
|
||||
void VoxelColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer();
|
||||
} else {
|
||||
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData(in, size));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
VoxelColorDataPointer data = decodeInline<VoxelColorDataPointer>(value);
|
||||
if (data) {
|
||||
data->write(out);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer();
|
||||
} else {
|
||||
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData(
|
||||
in, size, decodeInline<VoxelColorDataPointer>(reference)));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
VoxelColorDataPointer data = decodeInline<VoxelColorDataPointer>(value);
|
||||
if (data) {
|
||||
data->writeDelta(out, decodeInline<VoxelColorDataPointer>(reference));
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool VoxelColorAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
int maxSize = 0;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
VoxelColorDataPointer pointer = decodeInline<VoxelColorDataPointer>(children[i]);
|
||||
if (pointer) {
|
||||
maxSize = qMax(maxSize, pointer->getSize());
|
||||
}
|
||||
}
|
||||
if (maxSize == 0) {
|
||||
*(VoxelColorDataPointer*)&parent = VoxelColorDataPointer();
|
||||
return true;
|
||||
}
|
||||
int size = maxSize;
|
||||
int area = size * size;
|
||||
QVector<QRgb> contents(area * size);
|
||||
int halfSize = size / 2;
|
||||
int halfSizeComplement = size - halfSize;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
VoxelColorDataPointer child = decodeInline<VoxelColorDataPointer>(children[i]);
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
const QVector<QRgb>& childContents = child->getContents();
|
||||
int childSize = child->getSize();
|
||||
int childArea = childSize * childSize;
|
||||
const int INDEX_MASK = 1;
|
||||
int xIndex = i & INDEX_MASK;
|
||||
const int Y_SHIFT = 1;
|
||||
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
|
||||
int Z_SHIFT = 2;
|
||||
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
|
||||
QRgb* dest = contents.data() + (zIndex * halfSize * area) + (yIndex * halfSize * size) + (xIndex * halfSize);
|
||||
const QRgb* src = childContents.data();
|
||||
|
||||
const int MAX_ALPHA = 255;
|
||||
if (childSize == size) {
|
||||
// simple case: one destination value for four child values
|
||||
for (int z = 0; z < halfSizeComplement; z++) {
|
||||
int offset4 = (z == halfSize) ? 0 : childArea;
|
||||
for (int y = 0; y < halfSizeComplement; y++) {
|
||||
int offset2 = (y == halfSize) ? 0 : childSize;
|
||||
int offset6 = offset4 + offset2;
|
||||
for (QRgb* end = dest + halfSizeComplement; dest != end; ) {
|
||||
int offset1 = (dest == end - 1) ? 0 : 1;
|
||||
QRgb v0 = src[0], v1 = src[offset1], v2 = src[offset2], v3 = src[offset2 + offset1], v4 = src[offset4],
|
||||
v5 = src[offset4 + offset1], v6 = src[offset6], v7 = src[offset6 + offset1];
|
||||
src += (1 + offset1);
|
||||
int a0 = qAlpha(v0), a1 = qAlpha(v1), a2 = qAlpha(v2), a3 = qAlpha(v3),
|
||||
a4 = qAlpha(v4), a5 = qAlpha(v5), a6 = qAlpha(v6), a7 = qAlpha(v7);
|
||||
if (a0 == 0) {
|
||||
*dest++ = qRgba(0, 0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
int alphaTotal = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7;
|
||||
*dest++ = qRgba(
|
||||
(qRed(v0) * a0 + qRed(v1) * a1 + qRed(v2) * a2 + qRed(v3) * a3 +
|
||||
qRed(v4) * a4 + qRed(v5) * a5 + qRed(v6) * a6 + qRed(v7) * a7) / alphaTotal,
|
||||
(qGreen(v0) * a0 + qGreen(v1) * a1 + qGreen(v2) * a2 + qGreen(v3) * a3 +
|
||||
qGreen(v4) * a4 + qGreen(v5) * a5 + qGreen(v6) * a6 + qGreen(v7) * a7) / alphaTotal,
|
||||
(qBlue(v0) * a0 + qBlue(v1) * a1 + qBlue(v2) * a2 + qBlue(v3) * a3 +
|
||||
qBlue(v4) * a4 + qBlue(v5) * a5 + qBlue(v6) * a6 + qBlue(v7) * a7) / alphaTotal,
|
||||
MAX_ALPHA);
|
||||
}
|
||||
dest += halfSize;
|
||||
src += offset2;
|
||||
}
|
||||
dest += halfSize * size;
|
||||
src += offset4;
|
||||
}
|
||||
} else {
|
||||
// more complex: N destination values for four child values
|
||||
// ...
|
||||
}
|
||||
}
|
||||
*(VoxelColorDataPointer*)&parent = VoxelColorDataPointer(new VoxelColorData(contents, size));
|
||||
return false;
|
||||
}
|
||||
|
||||
const int VOXEL_MATERIAL_HEADER_SIZE = sizeof(qint32) * 6;
|
||||
|
||||
static QByteArray encodeVoxelMaterial(int offsetX, int offsetY, int offsetZ,
|
||||
int sizeX, int sizeY, int sizeZ, const QByteArray& contents) {
|
||||
QByteArray inflated(VOXEL_MATERIAL_HEADER_SIZE, 0);
|
||||
qint32* header = (qint32*)inflated.data();
|
||||
*header++ = offsetX;
|
||||
*header++ = offsetY;
|
||||
*header++ = offsetZ;
|
||||
*header++ = sizeX;
|
||||
*header++ = sizeY;
|
||||
*header++ = sizeZ;
|
||||
inflated.append(contents);
|
||||
return qCompress(inflated);
|
||||
}
|
||||
|
||||
static QByteArray decodeVoxelMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ,
|
||||
int& sizeX, int& sizeY, int& sizeZ) {
|
||||
QByteArray inflated = qUncompress(encoded);
|
||||
const qint32* header = (const qint32*)inflated.constData();
|
||||
offsetX = *header++;
|
||||
offsetY = *header++;
|
||||
offsetZ = *header++;
|
||||
sizeX = *header++;
|
||||
sizeY = *header++;
|
||||
sizeZ = *header++;
|
||||
return inflated.mid(VOXEL_MATERIAL_HEADER_SIZE);
|
||||
}
|
||||
|
||||
VoxelMaterialData::VoxelMaterialData(const QByteArray& contents, int size, const QVector<SharedObjectPointer>& materials) :
|
||||
_contents(contents),
|
||||
_size(size),
|
||||
_materials(materials) {
|
||||
}
|
||||
|
||||
VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes) {
|
||||
read(in, bytes);
|
||||
}
|
||||
|
||||
VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference) {
|
||||
if (!reference) {
|
||||
read(in, bytes);
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||
reference->setEncodedDelta(in.readAligned(bytes));
|
||||
in.readDelta(_materials, reference->getMaterials());
|
||||
reference->setDeltaData(DataBlockPointer(this));
|
||||
_contents = reference->getContents();
|
||||
_size = reference->getSize();
|
||||
|
||||
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
|
||||
QByteArray delta = decodeVoxelMaterial(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
|
||||
if (delta.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (offsetX == 0) {
|
||||
_contents = delta;
|
||||
_size = sizeX;
|
||||
return;
|
||||
}
|
||||
int minX = offsetX - 1;
|
||||
int minY = offsetY - 1;
|
||||
int minZ = offsetZ - 1;
|
||||
const char* src = delta.constData();
|
||||
int size2 = _size * _size;
|
||||
char* planeDest = _contents.data() + minZ * size2 + minY * _size + minX;
|
||||
for (int z = 0; z < sizeZ; z++, planeDest += size2) {
|
||||
char* dest = planeDest;
|
||||
for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) {
|
||||
memcpy(dest, src, sizeX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialData::write(Bitstream& out) {
|
||||
QMutexLocker locker(&_encodedMutex);
|
||||
if (_encoded.isEmpty()) {
|
||||
_encoded = encodeVoxelMaterial(0, 0, 0, _size, _size, _size, _contents);
|
||||
}
|
||||
out << _encoded.size();
|
||||
out.writeAligned(_encoded);
|
||||
out << _materials;
|
||||
}
|
||||
|
||||
void VoxelMaterialData::writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference) {
|
||||
if (!reference || reference->getSize() != _size) {
|
||||
write(out);
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
|
||||
int minX = _size, minY = _size, minZ = _size;
|
||||
int maxX = -1, maxY = -1, maxZ = -1;
|
||||
const char* src = _contents.constData();
|
||||
const char* ref = reference->getContents().constData();
|
||||
for (int z = 0; z < _size; z++) {
|
||||
bool differenceZ = false;
|
||||
for (int y = 0; y < _size; y++) {
|
||||
bool differenceY = false;
|
||||
for (int x = 0; x < _size; x++) {
|
||||
if (*src++ != *ref++) {
|
||||
minX = qMin(minX, x);
|
||||
maxX = qMax(maxX, x);
|
||||
differenceY = differenceZ = true;
|
||||
}
|
||||
}
|
||||
if (differenceY) {
|
||||
minY = qMin(minY, y);
|
||||
maxY = qMax(maxY, y);
|
||||
}
|
||||
}
|
||||
if (differenceZ) {
|
||||
minZ = qMin(minZ, z);
|
||||
maxZ = qMax(maxZ, z);
|
||||
}
|
||||
}
|
||||
QByteArray delta;
|
||||
int sizeX = 0, sizeY = 0, sizeZ = 0;
|
||||
if (maxX >= minX) {
|
||||
sizeX = maxX - minX + 1;
|
||||
sizeY = maxY - minY + 1;
|
||||
sizeZ = maxZ - minZ + 1;
|
||||
delta = QByteArray(sizeX * sizeY * sizeZ, 0);
|
||||
char* dest = delta.data();
|
||||
int size2 = _size * _size;
|
||||
const char* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX;
|
||||
for (int z = 0; z < sizeZ; z++, planeSrc += size2) {
|
||||
src = planeSrc;
|
||||
for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) {
|
||||
memcpy(dest, src, sizeX);
|
||||
}
|
||||
}
|
||||
}
|
||||
reference->setEncodedDelta(encodeVoxelMaterial(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
|
||||
reference->setDeltaData(DataBlockPointer(this));
|
||||
}
|
||||
out << reference->getEncodedDelta().size();
|
||||
out.writeAligned(reference->getEncodedDelta());
|
||||
out.writeDelta(_materials, reference->getMaterials());
|
||||
}
|
||||
|
||||
void VoxelMaterialData::read(Bitstream& in, int bytes) {
|
||||
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
|
||||
_contents = decodeVoxelMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
|
||||
_size = sizeX;
|
||||
in >> _materials;
|
||||
}
|
||||
|
||||
VoxelMaterialAttribute::VoxelMaterialAttribute(const QString& name) :
|
||||
InlineAttribute<VoxelMaterialDataPointer>(name) {
|
||||
}
|
||||
|
||||
void VoxelMaterialAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer();
|
||||
} else {
|
||||
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData(in, size));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
VoxelMaterialDataPointer data = decodeInline<VoxelMaterialDataPointer>(value);
|
||||
if (data) {
|
||||
data->write(out);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer();
|
||||
} else {
|
||||
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData(
|
||||
in, size, decodeInline<VoxelMaterialDataPointer>(reference)));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
VoxelMaterialDataPointer data = decodeInline<VoxelMaterialDataPointer>(value);
|
||||
if (data) {
|
||||
data->writeDelta(out, decodeInline<VoxelMaterialDataPointer>(reference));
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool VoxelMaterialAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
int maxSize = 0;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
VoxelMaterialDataPointer pointer = decodeInline<VoxelMaterialDataPointer>(children[i]);
|
||||
if (pointer) {
|
||||
maxSize = qMax(maxSize, pointer->getSize());
|
||||
}
|
||||
}
|
||||
*(VoxelMaterialDataPointer*)&parent = VoxelMaterialDataPointer();
|
||||
return maxSize == 0;
|
||||
}
|
||||
|
||||
VoxelHermiteData::VoxelHermiteData(const QVector<QRgb>& contents, int size) :
|
||||
_contents(contents),
|
||||
_size(size) {
|
||||
}
|
||||
|
||||
VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes) {
|
||||
read(in, bytes);
|
||||
}
|
||||
|
||||
VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference) {
|
||||
if (!reference) {
|
||||
read(in, bytes);
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||
reference->setEncodedDelta(in.readAligned(bytes));
|
||||
reference->setDeltaData(DataBlockPointer(this));
|
||||
_contents = reference->getContents();
|
||||
_size = reference->getSize();
|
||||
|
||||
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
|
||||
QVector<QRgb> delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
|
||||
if (delta.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (offsetX == 0) {
|
||||
_contents = delta;
|
||||
_size = sizeX;
|
||||
return;
|
||||
}
|
||||
int minX = offsetX - 1;
|
||||
int minY = offsetY - 1;
|
||||
int minZ = offsetZ - 1;
|
||||
const QRgb* src = delta.constData();
|
||||
int destStride = _size * EDGE_COUNT;
|
||||
int destStride2 = _size * destStride;
|
||||
QRgb* planeDest = _contents.data() + minZ * destStride2 + minY * destStride + minX * EDGE_COUNT;
|
||||
int srcStride = sizeX * EDGE_COUNT;
|
||||
int length = srcStride * sizeof(QRgb);
|
||||
for (int z = 0; z < sizeZ; z++, planeDest += destStride2) {
|
||||
QRgb* dest = planeDest;
|
||||
for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) {
|
||||
memcpy(dest, src, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelHermiteData::write(Bitstream& out) {
|
||||
QMutexLocker locker(&_encodedMutex);
|
||||
if (_encoded.isEmpty()) {
|
||||
_encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents);
|
||||
}
|
||||
out << _encoded.size();
|
||||
out.writeAligned(_encoded);
|
||||
}
|
||||
|
||||
void VoxelHermiteData::writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference) {
|
||||
if (!reference || reference->getSize() != _size) {
|
||||
write(out);
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
|
||||
int minX = _size, minY = _size, minZ = _size;
|
||||
int maxX = -1, maxY = -1, maxZ = -1;
|
||||
const QRgb* src = _contents.constData();
|
||||
const QRgb* ref = reference->getContents().constData();
|
||||
for (int z = 0; z < _size; z++) {
|
||||
bool differenceZ = false;
|
||||
for (int y = 0; y < _size; y++) {
|
||||
bool differenceY = false;
|
||||
for (int x = 0; x < _size; x++, src += EDGE_COUNT, ref += EDGE_COUNT) {
|
||||
if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) {
|
||||
minX = qMin(minX, x);
|
||||
maxX = qMax(maxX, x);
|
||||
differenceY = differenceZ = true;
|
||||
}
|
||||
}
|
||||
if (differenceY) {
|
||||
minY = qMin(minY, y);
|
||||
maxY = qMax(maxY, y);
|
||||
}
|
||||
}
|
||||
if (differenceZ) {
|
||||
minZ = qMin(minZ, z);
|
||||
maxZ = qMax(maxZ, z);
|
||||
}
|
||||
}
|
||||
QVector<QRgb> delta;
|
||||
int sizeX = 0, sizeY = 0, sizeZ = 0;
|
||||
if (maxX >= minX) {
|
||||
sizeX = maxX - minX + 1;
|
||||
sizeY = maxY - minY + 1;
|
||||
sizeZ = maxZ - minZ + 1;
|
||||
delta = QVector<QRgb>(sizeX * sizeY * sizeZ * EDGE_COUNT, 0);
|
||||
QRgb* dest = delta.data();
|
||||
int srcStride = _size * EDGE_COUNT;
|
||||
int srcStride2 = _size * srcStride;
|
||||
const QRgb* planeSrc = _contents.constData() + minZ * srcStride2 + minY * srcStride + minX * EDGE_COUNT;
|
||||
int destStride = sizeX * EDGE_COUNT;
|
||||
int length = destStride * sizeof(QRgb);
|
||||
for (int z = 0; z < sizeZ; z++, planeSrc += srcStride2) {
|
||||
src = planeSrc;
|
||||
for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) {
|
||||
memcpy(dest, src, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
|
||||
reference->setDeltaData(DataBlockPointer(this));
|
||||
}
|
||||
out << reference->getEncodedDelta().size();
|
||||
out.writeAligned(reference->getEncodedDelta());
|
||||
}
|
||||
|
||||
void VoxelHermiteData::read(Bitstream& in, int bytes) {
|
||||
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
|
||||
_contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
|
||||
_size = sizeX;
|
||||
}
|
||||
|
||||
VoxelHermiteAttribute::VoxelHermiteAttribute(const QString& name) :
|
||||
InlineAttribute<VoxelHermiteDataPointer>(name) {
|
||||
}
|
||||
|
||||
void VoxelHermiteAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer();
|
||||
} else {
|
||||
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(in, size));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelHermiteAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
VoxelHermiteDataPointer data = decodeInline<VoxelHermiteDataPointer>(value);
|
||||
if (data) {
|
||||
data->write(out);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelHermiteAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer();
|
||||
} else {
|
||||
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(
|
||||
in, size, decodeInline<VoxelHermiteDataPointer>(reference)));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelHermiteAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
|
||||
if (!isLeaf) {
|
||||
return;
|
||||
}
|
||||
VoxelHermiteDataPointer data = decodeInline<VoxelHermiteDataPointer>(value);
|
||||
if (data) {
|
||||
data->writeDelta(out, decodeInline<VoxelHermiteDataPointer>(reference));
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool VoxelHermiteAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
int maxSize = 0;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
VoxelHermiteDataPointer pointer = decodeInline<VoxelHermiteDataPointer>(children[i]);
|
||||
if (pointer) {
|
||||
maxSize = qMax(maxSize, pointer->getSize());
|
||||
}
|
||||
}
|
||||
if (maxSize == 0) {
|
||||
*(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer();
|
||||
return true;
|
||||
}
|
||||
int size = maxSize;
|
||||
int area = size * size;
|
||||
QVector<QRgb> contents(area * size * VoxelHermiteData::EDGE_COUNT);
|
||||
int halfSize = size / 2;
|
||||
int halfSizeComplement = size - halfSize;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
VoxelHermiteDataPointer child = decodeInline<VoxelHermiteDataPointer>(children[i]);
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
const QVector<QRgb>& childContents = child->getContents();
|
||||
int childSize = child->getSize();
|
||||
int childArea = childSize * childSize;
|
||||
const int INDEX_MASK = 1;
|
||||
int xIndex = i & INDEX_MASK;
|
||||
const int Y_SHIFT = 1;
|
||||
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
|
||||
int Z_SHIFT = 2;
|
||||
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
|
||||
QRgb* dest = contents.data() + ((zIndex * halfSize * area) + (yIndex * halfSize * size) + (xIndex * halfSize)) *
|
||||
VoxelHermiteData::EDGE_COUNT;
|
||||
const QRgb* src = childContents.data();
|
||||
int offsets[VoxelHermiteData::EDGE_COUNT];
|
||||
|
||||
if (childSize == size) {
|
||||
// simple case: one destination value for four child values
|
||||
for (int z = 0; z < halfSizeComplement; z++) {
|
||||
offsets[2] = (z == halfSize) ? 0 : (childArea * VoxelHermiteData::EDGE_COUNT);
|
||||
for (int y = 0; y < halfSizeComplement; y++) {
|
||||
offsets[1] = (y == halfSize) ? 0 : (childSize * VoxelHermiteData::EDGE_COUNT);
|
||||
for (QRgb* end = dest + halfSizeComplement * VoxelHermiteData::EDGE_COUNT; dest != end;
|
||||
dest += VoxelHermiteData::EDGE_COUNT) {
|
||||
offsets[0] = (dest == end - VoxelHermiteData::EDGE_COUNT) ? 0 : VoxelHermiteData::EDGE_COUNT;
|
||||
for (int i = 0; i < VoxelHermiteData::EDGE_COUNT; i++) {
|
||||
QRgb v0 = src[i], v1 = src[i + offsets[i]];
|
||||
glm::vec3 n0 = unpackNormal(v0), n1 = unpackNormal(v1);
|
||||
float l0 = glm::length(n0), l1 = glm::length(n1);
|
||||
float lengthTotal = l0 + l1;
|
||||
if (lengthTotal == 0.0f) {
|
||||
dest[i] = qRgba(0, 0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
glm::vec3 combinedNormal = n0 + n1;
|
||||
float combinedLength = glm::length(combinedNormal);
|
||||
if (combinedLength > 0.0f) {
|
||||
combinedNormal /= combinedLength;
|
||||
}
|
||||
float combinedOffset = qAlpha(v0) * 0.5f * l0 + (qAlpha(v1) + EIGHT_BIT_MAXIMUM) * 0.5f * l1;
|
||||
dest[i] = packNormal(combinedNormal, combinedOffset / lengthTotal);
|
||||
}
|
||||
src += (VoxelHermiteData::EDGE_COUNT + offsets[0]);
|
||||
}
|
||||
dest += (halfSize * VoxelHermiteData::EDGE_COUNT);
|
||||
src += offsets[1];
|
||||
}
|
||||
dest += (halfSize * size * VoxelHermiteData::EDGE_COUNT);
|
||||
src += offsets[2];
|
||||
}
|
||||
} else {
|
||||
// more complex: N destination values for four child values
|
||||
// ...
|
||||
}
|
||||
}
|
||||
*(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer(new VoxelHermiteData(contents, size));
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject,
|
||||
const SharedObjectPointer& defaultValue) :
|
||||
InlineAttribute<SharedObjectPointer>(name, defaultValue),
|
||||
|
|
|
@ -30,16 +30,10 @@ class QScriptValue;
|
|||
|
||||
class Attribute;
|
||||
class DataBlock;
|
||||
class HeightfieldColorData;
|
||||
class HeightfieldHeightData;
|
||||
class HeightfieldMaterialData;
|
||||
class MetavoxelData;
|
||||
class MetavoxelLOD;
|
||||
class MetavoxelNode;
|
||||
class MetavoxelStreamState;
|
||||
class VoxelColorData;
|
||||
class VoxelHermiteData;
|
||||
class VoxelMaterialData;
|
||||
|
||||
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
||||
|
||||
|
@ -88,24 +82,6 @@ public:
|
|||
/// Returns a reference to the standard SharedObjectSet "spanners" attribute.
|
||||
const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; }
|
||||
|
||||
/// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute.
|
||||
const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; }
|
||||
|
||||
/// Returns a reference to the standard HeightfieldColorDataPointer "heightfieldColor" attribute.
|
||||
const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; }
|
||||
|
||||
/// Returns a reference to the standard HeightfieldMaterialDataPointer "heightfieldMaterial" attribute.
|
||||
const AttributePointer& getHeightfieldMaterialAttribute() const { return _heightfieldMaterialAttribute; }
|
||||
|
||||
/// Returns a reference to the standard VoxelColorDataPointer "voxelColor" attribute.
|
||||
const AttributePointer& getVoxelColorAttribute() const { return _voxelColorAttribute; }
|
||||
|
||||
/// Returns a reference to the standard VoxelMaterialDataPointer "voxelMaterial" attribute.
|
||||
const AttributePointer& getVoxelMaterialAttribute() const { return _voxelMaterialAttribute; }
|
||||
|
||||
/// Returns a reference to the standard VoxelHermiteDataPointer "voxelHermite" attribute.
|
||||
const AttributePointer& getVoxelHermiteAttribute() const { return _voxelHermiteAttribute; }
|
||||
|
||||
private:
|
||||
|
||||
static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine);
|
||||
|
@ -116,12 +92,6 @@ private:
|
|||
AttributePointer _guideAttribute;
|
||||
AttributePointer _rendererAttribute;
|
||||
AttributePointer _spannersAttribute;
|
||||
AttributePointer _heightfieldAttribute;
|
||||
AttributePointer _heightfieldColorAttribute;
|
||||
AttributePointer _heightfieldMaterialAttribute;
|
||||
AttributePointer _voxelColorAttribute;
|
||||
AttributePointer _voxelMaterialAttribute;
|
||||
AttributePointer _voxelHermiteAttribute;
|
||||
};
|
||||
|
||||
/// Converts a value to a void pointer.
|
||||
|
@ -355,213 +325,6 @@ public:
|
|||
Q_INVOKABLE FloatAttribute(const QString& name = QString());
|
||||
};
|
||||
|
||||
/// Packs a normal into an RGB value.
|
||||
QRgb packNormal(const glm::vec3& normal);
|
||||
|
||||
/// Packs a normal (plus extra alpha value) into an RGBA value.
|
||||
QRgb packNormal(const glm::vec3& normal, int alpha);
|
||||
|
||||
/// Unpacks a normal from an RGB value.
|
||||
glm::vec3 unpackNormal(QRgb value);
|
||||
|
||||
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
|
||||
|
||||
/// Base class for blocks of data.
|
||||
class DataBlock : public QSharedData {
|
||||
public:
|
||||
|
||||
static const int COLOR_BYTES = 3;
|
||||
|
||||
virtual ~DataBlock();
|
||||
|
||||
void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; }
|
||||
const DataBlockPointer& getDeltaData() const { return _deltaData; }
|
||||
|
||||
void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
|
||||
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
|
||||
|
||||
QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; }
|
||||
|
||||
protected:
|
||||
|
||||
QByteArray _encoded;
|
||||
QMutex _encodedMutex;
|
||||
|
||||
DataBlockPointer _deltaData;
|
||||
QByteArray _encodedDelta;
|
||||
QMutex _encodedDeltaMutex;
|
||||
|
||||
class EncodedSubdivision {
|
||||
public:
|
||||
DataBlockPointer ancestor;
|
||||
QByteArray data;
|
||||
};
|
||||
QVector<EncodedSubdivision> _encodedSubdivisions;
|
||||
QMutex _encodedSubdivisionsMutex;
|
||||
};
|
||||
|
||||
/// Contains the description of a material.
|
||||
class MaterialObject : public SharedObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QUrl diffuse MEMBER _diffuse)
|
||||
Q_PROPERTY(float scaleS MEMBER _scaleS)
|
||||
Q_PROPERTY(float scaleT MEMBER _scaleT)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE MaterialObject();
|
||||
|
||||
const QUrl& getDiffuse() const { return _diffuse; }
|
||||
|
||||
float getScaleS() const { return _scaleS; }
|
||||
float getScaleT() const { return _scaleT; }
|
||||
|
||||
private:
|
||||
|
||||
QUrl _diffuse;
|
||||
float _scaleS;
|
||||
float _scaleT;
|
||||
};
|
||||
|
||||
/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index,
|
||||
/// creating a new entry in the list if necessary.
|
||||
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents);
|
||||
|
||||
/// Utility method for editing: removes any unused materials from the supplied list.
|
||||
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents);
|
||||
|
||||
typedef QExplicitlySharedDataPointer<VoxelColorData> VoxelColorDataPointer;
|
||||
|
||||
/// Contains a block of voxel color data.
|
||||
class VoxelColorData : public DataBlock {
|
||||
public:
|
||||
|
||||
VoxelColorData(const QVector<QRgb>& contents, int size);
|
||||
VoxelColorData(Bitstream& in, int bytes);
|
||||
VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference);
|
||||
|
||||
const QVector<QRgb>& getContents() const { return _contents; }
|
||||
|
||||
int getSize() const { return _size; }
|
||||
|
||||
void write(Bitstream& out);
|
||||
void writeDelta(Bitstream& out, const VoxelColorDataPointer& reference);
|
||||
|
||||
private:
|
||||
|
||||
void read(Bitstream& in, int bytes);
|
||||
|
||||
QVector<QRgb> _contents;
|
||||
int _size;
|
||||
};
|
||||
|
||||
/// An attribute that stores voxel colors.
|
||||
class VoxelColorAttribute : public InlineAttribute<VoxelColorDataPointer> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE VoxelColorAttribute(const QString& name = QString());
|
||||
|
||||
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
|
||||
|
||||
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
|
||||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<VoxelMaterialData> VoxelMaterialDataPointer;
|
||||
|
||||
/// Contains a block of voxel material data.
|
||||
class VoxelMaterialData : public DataBlock {
|
||||
public:
|
||||
|
||||
VoxelMaterialData(const QByteArray& contents, int size,
|
||||
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
|
||||
VoxelMaterialData(Bitstream& in, int bytes);
|
||||
VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference);
|
||||
|
||||
const QByteArray& getContents() const { return _contents; }
|
||||
|
||||
int getSize() const { return _size; }
|
||||
|
||||
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
|
||||
|
||||
void write(Bitstream& out);
|
||||
void writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference);
|
||||
|
||||
private:
|
||||
|
||||
void read(Bitstream& in, int bytes);
|
||||
|
||||
QByteArray _contents;
|
||||
int _size;
|
||||
QVector<SharedObjectPointer> _materials;
|
||||
};
|
||||
|
||||
/// An attribute that stores voxel materials.
|
||||
class VoxelMaterialAttribute : public InlineAttribute<VoxelMaterialDataPointer> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE VoxelMaterialAttribute(const QString& name = QString());
|
||||
|
||||
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
|
||||
|
||||
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
|
||||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<VoxelHermiteData> VoxelHermiteDataPointer;
|
||||
|
||||
/// Contains a block of voxel Hermite data (positions and normals at edge crossings).
|
||||
class VoxelHermiteData : public DataBlock {
|
||||
public:
|
||||
|
||||
static const int EDGE_COUNT = 3;
|
||||
|
||||
VoxelHermiteData(const QVector<QRgb>& contents, int size);
|
||||
VoxelHermiteData(Bitstream& in, int bytes);
|
||||
VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference);
|
||||
|
||||
const QVector<QRgb>& getContents() const { return _contents; }
|
||||
|
||||
int getSize() const { return _size; }
|
||||
|
||||
void write(Bitstream& out);
|
||||
void writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference);
|
||||
|
||||
private:
|
||||
|
||||
void read(Bitstream& in, int bytes);
|
||||
|
||||
QVector<QRgb> _contents;
|
||||
int _size;
|
||||
};
|
||||
|
||||
/// An attribute that stores voxel Hermite data.
|
||||
class VoxelHermiteAttribute : public InlineAttribute<VoxelHermiteDataPointer> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE VoxelHermiteAttribute(const QString& name = QString());
|
||||
|
||||
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
|
||||
|
||||
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
|
||||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) 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
|
||||
|
|
|
@ -2432,12 +2432,13 @@ void MappedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object,
|
|||
}
|
||||
|
||||
QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const {
|
||||
bool reread = (object != NULL);
|
||||
if (!object && _metaObject) {
|
||||
object = _metaObject->newInstance();
|
||||
}
|
||||
foreach (const StreamerPropertyPair& property, _properties) {
|
||||
QVariant value = property.first->read(in);
|
||||
if (property.second.isValid() && object) {
|
||||
if (property.second.isValid() && object && !reread) {
|
||||
property.second.write(object, value);
|
||||
}
|
||||
}
|
||||
|
@ -2445,6 +2446,7 @@ QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const {
|
|||
}
|
||||
|
||||
QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const {
|
||||
bool reread = (object != NULL);
|
||||
if (!object && _metaObject) {
|
||||
object = _metaObject->newInstance();
|
||||
}
|
||||
|
@ -2452,7 +2454,7 @@ QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* refere
|
|||
QVariant value;
|
||||
property.first->readDelta(in, value, (property.second.isValid() && reference &&
|
||||
reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant());
|
||||
if (property.second.isValid() && object) {
|
||||
if (property.second.isValid() && object && !reread) {
|
||||
property.second.write(object, value);
|
||||
}
|
||||
}
|
||||
|
@ -2475,13 +2477,13 @@ void SharedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object,
|
|||
|
||||
QObject* SharedObjectStreamer::read(Bitstream& in, QObject* object) const {
|
||||
QObject* result = MappedObjectStreamer::read(in, object);
|
||||
static_cast<SharedObject*>(result)->readExtra(in);
|
||||
static_cast<SharedObject*>(result)->readExtra(in, object != NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
QObject* SharedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const {
|
||||
QObject* result = MappedObjectStreamer::readRawDelta(in, reference, object);
|
||||
static_cast<SharedObject*>(result)->readExtraDelta(in, static_cast<const SharedObject*>(reference));
|
||||
static_cast<SharedObject*>(result)->readExtraDelta(in, static_cast<const SharedObject*>(reference), object != NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2592,6 +2594,7 @@ void GenericObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object,
|
|||
}
|
||||
|
||||
QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const {
|
||||
bool reread = (object != NULL);
|
||||
if (!object) {
|
||||
object = new GenericSharedObject(_weakSelf);
|
||||
}
|
||||
|
@ -2599,11 +2602,14 @@ QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const {
|
|||
foreach (const StreamerNamePair& property, _properties) {
|
||||
values.append(property.first->read(in));
|
||||
}
|
||||
static_cast<GenericSharedObject*>(object)->setValues(values);
|
||||
if (!reread) {
|
||||
static_cast<GenericSharedObject*>(object)->setValues(values);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const {
|
||||
bool reread = (object != NULL);
|
||||
if (!object) {
|
||||
object = new GenericSharedObject(_weakSelf);
|
||||
}
|
||||
|
@ -2615,7 +2621,9 @@ QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* refer
|
|||
static_cast<const GenericSharedObject*>(reference)->getValues().at(i) : QVariant());
|
||||
values.append(value);
|
||||
}
|
||||
static_cast<GenericSharedObject*>(object)->setValues(values);
|
||||
if (!reread) {
|
||||
static_cast<GenericSharedObject*>(object)->setValues(values);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
|
|
|
@ -715,8 +715,9 @@ void ReliableChannel::endMessage() {
|
|||
|
||||
quint32 length = _buffer.pos() - _messageLengthPlaceholder;
|
||||
_buffer.writeBytes(_messageLengthPlaceholder, sizeof(quint32), (const char*)&length);
|
||||
_messageReceivedOffset = getBytesWritten();
|
||||
_messageSize = length;
|
||||
|
||||
pruneOutgoingMessageStats();
|
||||
_outgoingMessageStats.append(OffsetSizePair(getBytesWritten(), length));
|
||||
}
|
||||
|
||||
void ReliableChannel::sendMessage(const QVariant& message) {
|
||||
|
@ -725,12 +726,14 @@ void ReliableChannel::sendMessage(const QVariant& message) {
|
|||
endMessage();
|
||||
}
|
||||
|
||||
bool ReliableChannel::getMessageSendProgress(int& sent, int& total) const {
|
||||
if (!_messagesEnabled || _offset >= _messageReceivedOffset) {
|
||||
bool ReliableChannel::getMessageSendProgress(int& sent, int& total) {
|
||||
pruneOutgoingMessageStats();
|
||||
if (!_messagesEnabled || _outgoingMessageStats.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
sent = qMax(0, _messageSize - (_messageReceivedOffset - _offset));
|
||||
total = _messageSize;
|
||||
const OffsetSizePair& stat = _outgoingMessageStats.first();
|
||||
sent = qMax(0, stat.second - (stat.first - _offset));
|
||||
total = stat.second;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -770,8 +773,7 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o
|
|||
_offset(0),
|
||||
_writePosition(0),
|
||||
_writePositionResetPacketNumber(0),
|
||||
_messagesEnabled(true),
|
||||
_messageReceivedOffset(0) {
|
||||
_messagesEnabled(true) {
|
||||
|
||||
_buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly);
|
||||
_dataStream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
@ -942,3 +944,9 @@ void ReliableChannel::readData(QDataStream& in) {
|
|||
}
|
||||
}
|
||||
|
||||
void ReliableChannel::pruneOutgoingMessageStats() {
|
||||
while (!_outgoingMessageStats.isEmpty() && _offset >= _outgoingMessageStats.first().first) {
|
||||
_outgoingMessageStats.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -386,7 +386,7 @@ public:
|
|||
|
||||
/// Determines the number of bytes uploaded towards the currently pending message.
|
||||
/// \return true if there is a message pending, in which case the sent and total arguments will be set
|
||||
bool getMessageSendProgress(int& sent, int& total) const;
|
||||
bool getMessageSendProgress(int& sent, int& total);
|
||||
|
||||
/// Determines the number of bytes downloaded towards the currently pending message.
|
||||
/// \return true if there is a message pending, in which case the received and total arguments will be set
|
||||
|
@ -418,6 +418,8 @@ private:
|
|||
|
||||
void readData(QDataStream& in);
|
||||
|
||||
void pruneOutgoingMessageStats();
|
||||
|
||||
int _index;
|
||||
bool _output;
|
||||
CircularBuffer _buffer;
|
||||
|
@ -432,6 +434,10 @@ private:
|
|||
SpanList _acknowledged;
|
||||
bool _messagesEnabled;
|
||||
int _messageLengthPlaceholder; ///< the location in the buffer of the message length for the current message
|
||||
|
||||
typedef QPair<int, int> OffsetSizePair;
|
||||
QVector<OffsetSizePair> _outgoingMessageStats;
|
||||
|
||||
int _messageReceivedOffset; ///< when reached, indicates that the most recent sent message has been received
|
||||
int _messageSize; ///< the size of the most recent sent message; only valid when _messageReceivedOffset has been set
|
||||
};
|
||||
|
|
|
@ -29,7 +29,9 @@ MetavoxelLOD::MetavoxelLOD(const glm::vec3& position, float threshold) :
|
|||
}
|
||||
|
||||
bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size, float multiplier) const {
|
||||
return size >= glm::distance(position, minimum + glm::vec3(size, size, size) * 0.5f) * threshold * multiplier;
|
||||
float halfSize = size * 0.5f;
|
||||
return size >= (glm::distance(position, minimum + glm::vec3(halfSize, halfSize, halfSize)) - halfSize) *
|
||||
threshold * multiplier;
|
||||
}
|
||||
|
||||
bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size,
|
||||
|
@ -57,7 +59,9 @@ bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec3& minimum, float s
|
|||
}
|
||||
|
||||
bool MetavoxelLOD::shouldSubdivide(const glm::vec2& minimum, float size, float multiplier) const {
|
||||
return size >= glm::distance(glm::vec2(position), minimum + glm::vec2(size, size) * 0.5f) * threshold * multiplier;
|
||||
float halfSize = size * 0.5f;
|
||||
return size >= (glm::distance(glm::vec2(position), minimum + glm::vec2(halfSize, halfSize)) - halfSize) *
|
||||
threshold * multiplier;
|
||||
}
|
||||
|
||||
bool MetavoxelLOD::becameSubdivided(const glm::vec2& minimum, float size,
|
||||
|
@ -1188,67 +1192,6 @@ void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const {
|
|||
}
|
||||
}
|
||||
|
||||
void MetavoxelNode::writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const {
|
||||
SharedObjectSet oldSet = decodeInline<SharedObjectSet>(reference.getAttributeValue());
|
||||
SharedObjectSet newSet = decodeInline<SharedObjectSet>(_attributeValue);
|
||||
foreach (const SharedObjectPointer& object, oldSet) {
|
||||
if (static_cast<Spanner*>(object.data())->testAndSetVisited(state.base.visit) && !newSet.contains(object)) {
|
||||
state.base.stream << object;
|
||||
}
|
||||
}
|
||||
foreach (const SharedObjectPointer& object, newSet) {
|
||||
if (static_cast<Spanner*>(object.data())->testAndSetVisited(state.base.visit) && !oldSet.contains(object)) {
|
||||
state.base.stream << object;
|
||||
}
|
||||
}
|
||||
if (isLeaf() || !state.shouldSubdivide()) {
|
||||
if (!reference.isLeaf() && state.shouldSubdivideReference()) {
|
||||
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
nextState.setMinimum(state.minimum, i);
|
||||
reference._children[i]->writeSpanners(nextState);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
|
||||
if (reference.isLeaf() || !state.shouldSubdivideReference()) {
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
nextState.setMinimum(state.minimum, i);
|
||||
_children[i]->writeSpanners(nextState);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
nextState.setMinimum(state.minimum, i);
|
||||
if (_children[i] != reference._children[i]) {
|
||||
_children[i]->writeSpannerDelta(*reference._children[i], nextState);
|
||||
|
||||
} else if (nextState.becameSubdivided()) {
|
||||
_children[i]->writeSpannerSubdivision(nextState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelNode::writeSpannerSubdivision(MetavoxelStreamState& state) const {
|
||||
if (!isLeaf()) {
|
||||
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
|
||||
if (!state.shouldSubdivideReference()) {
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
nextState.setMinimum(state.minimum, i);
|
||||
_children[i]->writeSpanners(nextState);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
nextState.setMinimum(state.minimum, i);
|
||||
if (nextState.becameSubdivided()) {
|
||||
_children[i]->writeSpannerSubdivision(nextState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) {
|
||||
if (!_referenceCount.deref()) {
|
||||
destroy(attribute);
|
||||
|
@ -1621,8 +1564,9 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, int e
|
|||
|
||||
bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
||||
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
|
||||
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
||||
visitation.visitor->getLOD().threshold;
|
||||
float halfSize = visitation.info.size * 0.5f;
|
||||
visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum +
|
||||
glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold;
|
||||
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
|
||||
visitation.visitor->getMinimumLODThresholdMultiplier());
|
||||
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
|
||||
|
@ -1651,8 +1595,9 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
|||
|
||||
bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
||||
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
|
||||
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
||||
visitation.visitor->getLOD().threshold;
|
||||
float halfSize = visitation.info.size * 0.5f;
|
||||
visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum +
|
||||
glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold;
|
||||
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
|
||||
visitation.visitor->getMinimumLODThresholdMultiplier());
|
||||
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
|
||||
|
|
|
@ -239,9 +239,7 @@ public:
|
|||
void writeSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, void* ancestorValue) const;
|
||||
|
||||
void writeSpanners(MetavoxelStreamState& state) const;
|
||||
void writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const;
|
||||
void writeSpannerSubdivision(MetavoxelStreamState& state) const;
|
||||
|
||||
|
||||
/// Increments the node's reference count.
|
||||
void incrementReferenceCount() { _referenceCount.ref(); }
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "MetavoxelMessages.h"
|
||||
#include "Spanner.h"
|
||||
|
||||
|
@ -146,10 +148,13 @@ void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects
|
|||
data.set(minimum, this->data, blend);
|
||||
}
|
||||
|
||||
PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, float height) :
|
||||
PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius,
|
||||
float height, bool set, bool erase) :
|
||||
position(position),
|
||||
radius(radius),
|
||||
height(height) {
|
||||
height(height),
|
||||
set(set),
|
||||
erase(erase) {
|
||||
}
|
||||
|
||||
void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
|
@ -161,7 +166,7 @@ void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObje
|
|||
Box(position - extents, position + extents), results);
|
||||
|
||||
foreach (const SharedObjectPointer& spanner, results) {
|
||||
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintHeight(position, radius, height);
|
||||
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintHeight(position, radius, height, set, erase);
|
||||
if (newSpanner != spanner) {
|
||||
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
|
||||
}
|
||||
|
@ -173,473 +178,100 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av
|
|||
averageColor(averageColor) {
|
||||
}
|
||||
|
||||
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& averageColor) :
|
||||
HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner,
|
||||
const SharedObjectPointer& material, const QColor& averageColor, bool paint, bool voxelize) :
|
||||
MaterialEdit(material, averageColor),
|
||||
spanner(spanner),
|
||||
paint(paint),
|
||||
voxelize(voxelize) {
|
||||
}
|
||||
|
||||
class SpannerProjectionFetchVisitor : public SpannerVisitor {
|
||||
public:
|
||||
|
||||
SpannerProjectionFetchVisitor(const Box& bounds, QVector<SharedObjectPointer>& results);
|
||||
|
||||
virtual bool visit(Spanner* spanner);
|
||||
|
||||
private:
|
||||
|
||||
const Box& _bounds;
|
||||
QVector<SharedObjectPointer>& _results;
|
||||
float _closestDistance;
|
||||
};
|
||||
|
||||
SpannerProjectionFetchVisitor::SpannerProjectionFetchVisitor(const Box& bounds, QVector<SharedObjectPointer>& results) :
|
||||
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute()),
|
||||
_bounds(bounds),
|
||||
_results(results),
|
||||
_closestDistance(FLT_MAX) {
|
||||
}
|
||||
|
||||
bool SpannerProjectionFetchVisitor::visit(Spanner* spanner) {
|
||||
Heightfield* heightfield = qobject_cast<Heightfield*>(spanner);
|
||||
if (!heightfield) {
|
||||
return true;
|
||||
}
|
||||
glm::mat4 transform = glm::scale(1.0f / glm::vec3(heightfield->getScale(),
|
||||
heightfield->getScale() * heightfield->getAspectY(),
|
||||
heightfield->getScale() * heightfield->getAspectZ())) *
|
||||
glm::mat4_cast(glm::inverse(heightfield->getRotation())) * glm::translate(-heightfield->getTranslation());
|
||||
Box transformedBounds = transform * _bounds;
|
||||
if (transformedBounds.maximum.x < 0.0f || transformedBounds.maximum.z < 0.0f ||
|
||||
transformedBounds.minimum.x > 1.0f || transformedBounds.minimum.z > 1.0f) {
|
||||
return true;
|
||||
}
|
||||
float distance = qMin(glm::abs(transformedBounds.minimum.y), glm::abs(transformedBounds.maximum.y));
|
||||
if (distance < _closestDistance) {
|
||||
_results.clear();
|
||||
_results.append(spanner);
|
||||
_closestDistance = distance;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
// make sure the color meets our transparency requirements
|
||||
QColor color = averageColor;
|
||||
if (paint) {
|
||||
color.setAlphaF(1.0f);
|
||||
|
||||
} else if (color.alphaF() < 0.5f) {
|
||||
color = QColor(0, 0, 0, 0);
|
||||
}
|
||||
QVector<SharedObjectPointer> results;
|
||||
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||
static_cast<Spanner*>(spanner.data())->getBounds(), results);
|
||||
|
||||
// if there's nothing intersecting directly, find the closest heightfield that intersects the projection
|
||||
if (results.isEmpty()) {
|
||||
SpannerProjectionFetchVisitor visitor(static_cast<Spanner*>(spanner.data())->getBounds(), results);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
||||
foreach (const SharedObjectPointer& result, results) {
|
||||
Spanner* newResult = static_cast<Spanner*>(result.data())->setMaterial(spanner, material, color, paint, voxelize);
|
||||
if (newResult != result) {
|
||||
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FillHeightfieldHeightEdit::FillHeightfieldHeightEdit(const glm::vec3& position, float radius) :
|
||||
position(position),
|
||||
radius(radius) {
|
||||
}
|
||||
|
||||
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
glm::vec3 extents(radius, radius, radius);
|
||||
void FillHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
glm::vec3 extents = glm::vec3(radius, radius, radius);
|
||||
QVector<SharedObjectPointer> results;
|
||||
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||
Box(position - extents, position + extents), results);
|
||||
|
||||
foreach (const SharedObjectPointer& spanner, results) {
|
||||
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintMaterial(position, radius, material, averageColor);
|
||||
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->fillHeight(position, radius);
|
||||
if (newSpanner != spanner) {
|
||||
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int VOXEL_BLOCK_SIZE = 16;
|
||||
const int VOXEL_BLOCK_SAMPLES = VOXEL_BLOCK_SIZE + 1;
|
||||
const int VOXEL_BLOCK_AREA = VOXEL_BLOCK_SAMPLES * VOXEL_BLOCK_SAMPLES;
|
||||
const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES;
|
||||
|
||||
VoxelMaterialSpannerEdit::VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner,
|
||||
const SharedObjectPointer& material, const QColor& averageColor) :
|
||||
MaterialEdit(material, averageColor),
|
||||
spanner(spanner) {
|
||||
}
|
||||
|
||||
class VoxelMaterialSpannerEditVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
VoxelMaterialSpannerEditVisitor(Spanner* spanner, const SharedObjectPointer& material, const QColor& color);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
Spanner* _spanner;
|
||||
SharedObjectPointer _material;
|
||||
QColor _color;
|
||||
float _blockSize;
|
||||
};
|
||||
|
||||
VoxelMaterialSpannerEditVisitor::VoxelMaterialSpannerEditVisitor(Spanner* spanner,
|
||||
const SharedObjectPointer& material, const QColor& color) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
|
||||
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
|
||||
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
|
||||
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
|
||||
_spanner(spanner),
|
||||
_material(material),
|
||||
_color(color),
|
||||
_blockSize(spanner->getVoxelizationGranularity() * VOXEL_BLOCK_SIZE) {
|
||||
}
|
||||
|
||||
int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
|
||||
Box bounds = info.getBounds();
|
||||
if (!bounds.intersects(_spanner->getBounds())) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (info.size > _blockSize) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
QVector<QRgb> oldColorContents;
|
||||
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
|
||||
if (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
|
||||
oldColorContents = colorPointer->getContents();
|
||||
} else {
|
||||
oldColorContents = QVector<QRgb>(VOXEL_BLOCK_VOLUME);
|
||||
}
|
||||
|
||||
QVector<QRgb> hermiteContents;
|
||||
VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue<VoxelHermiteDataPointer>();
|
||||
if (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) {
|
||||
hermiteContents = hermitePointer->getContents();
|
||||
} else {
|
||||
hermiteContents = QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
|
||||
}
|
||||
|
||||
QByteArray materialContents;
|
||||
QVector<SharedObjectPointer> materials;
|
||||
VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<VoxelMaterialDataPointer>();
|
||||
if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
|
||||
materialContents = materialPointer->getContents();
|
||||
materials = materialPointer->getMaterials();
|
||||
} else {
|
||||
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
|
||||
}
|
||||
|
||||
float scale = VOXEL_BLOCK_SIZE / info.size;
|
||||
QVector<QRgb> colorContents = oldColorContents;
|
||||
|
||||
Box overlap = info.getBounds().getIntersection(_spanner->getBounds());
|
||||
overlap.minimum = (overlap.minimum - info.minimum) * scale;
|
||||
overlap.maximum = (overlap.maximum - info.minimum) * scale;
|
||||
int minX = glm::ceil(overlap.minimum.x);
|
||||
int minY = glm::ceil(overlap.minimum.y);
|
||||
int minZ = glm::ceil(overlap.minimum.z);
|
||||
int sizeX = (int)overlap.maximum.x - minX + 1;
|
||||
int sizeY = (int)overlap.maximum.y - minY + 1;
|
||||
int sizeZ = (int)overlap.maximum.z - minZ + 1;
|
||||
|
||||
bool flipped = false;
|
||||
float step = 1.0f / scale;
|
||||
glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step);
|
||||
if (_spanner->hasOwnColors()) {
|
||||
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 += step) {
|
||||
position.y = info.minimum.y + minY * step;
|
||||
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
|
||||
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
|
||||
position.x = info.minimum.x + minX * step;
|
||||
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
|
||||
if (_spanner->contains(position)) {
|
||||
*destX = _spanner->getColorAt(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QRgb rgb = _color.rgba();
|
||||
flipped = (qAlpha(rgb) == 0);
|
||||
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 += step) {
|
||||
position.y = info.minimum.y + minY * step;
|
||||
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
|
||||
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
|
||||
position.x = info.minimum.x + minX * step;
|
||||
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
|
||||
if (_spanner->contains(position)) {
|
||||
*destX = rgb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there are no visible colors, we can clear everything
|
||||
bool foundOpaque = false;
|
||||
for (const QRgb* src = colorContents.constData(), *end = src + colorContents.size(); src != end; src++) {
|
||||
if (qAlpha(*src) != 0) {
|
||||
foundOpaque = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundOpaque) {
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0));
|
||||
info.outputValues[1] = AttributeValue(_outputs.at(1));
|
||||
info.outputValues[2] = AttributeValue(_outputs.at(2));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES));
|
||||
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
|
||||
encodeInline<VoxelColorDataPointer>(newColorPointer));
|
||||
|
||||
int hermiteArea = VOXEL_BLOCK_AREA * VoxelHermiteData::EDGE_COUNT;
|
||||
int hermiteSamples = VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT;
|
||||
int hermiteMinX = minX, hermiteMinY = minY, hermiteMinZ = minZ;
|
||||
int hermiteSizeX = sizeX, hermiteSizeY = sizeY, hermiteSizeZ = sizeZ;
|
||||
if (minX > 0) {
|
||||
hermiteMinX--;
|
||||
hermiteSizeX++;
|
||||
}
|
||||
if (minY > 0) {
|
||||
hermiteMinY--;
|
||||
hermiteSizeY++;
|
||||
}
|
||||
if (minZ > 0) {
|
||||
hermiteMinZ--;
|
||||
hermiteSizeZ++;
|
||||
}
|
||||
const int EIGHT_BIT_MAXIMUM = 255;
|
||||
QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples +
|
||||
hermiteMinX * VoxelHermiteData::EDGE_COUNT;
|
||||
for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) {
|
||||
QRgb* hermiteDestY = hermiteDestZ;
|
||||
for (int y = hermiteMinY, hermiteMaxY = y + hermiteSizeY - 1; y <= hermiteMaxY; y++, hermiteDestY += hermiteSamples) {
|
||||
QRgb* hermiteDestX = hermiteDestY;
|
||||
for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++,
|
||||
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
|
||||
float distance;
|
||||
glm::vec3 normal;
|
||||
if (x != VOXEL_BLOCK_SIZE) {
|
||||
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
|
||||
const QRgb* color = colorContents.constData() + offset;
|
||||
int alpha0 = qAlpha(color[0]);
|
||||
int alpha1 = qAlpha(color[1]);
|
||||
if (alpha0 != alpha1) {
|
||||
if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step,
|
||||
info.minimum + glm::vec3(x + 1, y, z) * step, distance, normal)) {
|
||||
const QRgb* oldColor = oldColorContents.constData() + offset;
|
||||
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[1]) == alpha1) {
|
||||
int alpha = distance * 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, distance * EIGHT_BIT_MAXIMUM);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hermiteDestX[0] = 0x0;
|
||||
}
|
||||
} else {
|
||||
hermiteDestX[0] = 0x0;
|
||||
}
|
||||
if (y != VOXEL_BLOCK_SIZE) {
|
||||
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
|
||||
const QRgb* color = colorContents.constData() + offset;
|
||||
int alpha0 = qAlpha(color[0]);
|
||||
int alpha2 = qAlpha(color[VOXEL_BLOCK_SAMPLES]);
|
||||
if (alpha0 != alpha2) {
|
||||
if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step,
|
||||
info.minimum + glm::vec3(x, y + 1, z) * step, distance, normal)) {
|
||||
const QRgb* oldColor = oldColorContents.constData() + offset;
|
||||
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_SAMPLES]) == alpha2) {
|
||||
int alpha = distance * 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, distance * EIGHT_BIT_MAXIMUM);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hermiteDestX[1] = 0x0;
|
||||
}
|
||||
} else {
|
||||
hermiteDestX[1] = 0x0;
|
||||
}
|
||||
if (z != VOXEL_BLOCK_SIZE) {
|
||||
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
|
||||
const QRgb* color = colorContents.constData() + offset;
|
||||
int alpha0 = qAlpha(color[0]);
|
||||
int alpha4 = qAlpha(color[VOXEL_BLOCK_AREA]);
|
||||
if (alpha0 != alpha4) {
|
||||
if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step,
|
||||
info.minimum + glm::vec3(x, y, z + 1) * step, distance, normal)) {
|
||||
const QRgb* oldColor = oldColorContents.constData() + offset;
|
||||
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_AREA]) == alpha4) {
|
||||
int alpha = distance * 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, distance * EIGHT_BIT_MAXIMUM);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hermiteDestX[2] = 0x0;
|
||||
}
|
||||
} else {
|
||||
hermiteDestX[2] = 0x0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES));
|
||||
info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(),
|
||||
encodeInline<VoxelHermiteDataPointer>(newHermitePointer));
|
||||
|
||||
if (_spanner->hasOwnMaterials()) {
|
||||
QHash<int, int> materialMap;
|
||||
position.z = info.minimum.z + minZ * step;
|
||||
for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
|
||||
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
|
||||
position.y = info.minimum.y + minY * step;
|
||||
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
|
||||
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
|
||||
position.x = info.minimum.x + minX * step;
|
||||
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
|
||||
if (_spanner->contains(position)) {
|
||||
int material = _spanner->getMaterialAt(position);
|
||||
if (material != 0) {
|
||||
int& mapping = materialMap[material];
|
||||
if (mapping == 0) {
|
||||
mapping = getMaterialIndex(_spanner->getMaterials().at(material - 1), materials,
|
||||
materialContents);
|
||||
}
|
||||
material = mapping;
|
||||
}
|
||||
*destX = material;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
|
||||
position.z = info.minimum.z + minZ * step;
|
||||
for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
|
||||
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
|
||||
position.y = info.minimum.y + minY * step;
|
||||
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
|
||||
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
|
||||
position.x = info.minimum.x + minX * step;
|
||||
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
|
||||
if (_spanner->contains(position)) {
|
||||
*destX = materialIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clearUnusedMaterials(materials, materialContents);
|
||||
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES, materials));
|
||||
info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
|
||||
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
// expand to fit the entire edit
|
||||
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
|
||||
while (!data.getBounds().contains(spanner->getBounds())) {
|
||||
data.expand();
|
||||
}
|
||||
// make sure it's either 100% transparent or 100% opaque
|
||||
QColor color = averageColor;
|
||||
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
|
||||
|
||||
// find the bounds of all voxel nodes intersected
|
||||
float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor(
|
||||
glm::log(spanner->getVoxelizationGranularity()) / glm::log(2.0f)));
|
||||
Box bounds(glm::floor(spanner->getBounds().minimum / nodeSize) * nodeSize,
|
||||
glm::ceil(spanner->getBounds().maximum / nodeSize) * nodeSize);
|
||||
|
||||
// expand to include edges
|
||||
Box expandedBounds = bounds;
|
||||
float increment = nodeSize / VOXEL_BLOCK_SIZE;
|
||||
expandedBounds.maximum.x += increment;
|
||||
expandedBounds.maximum.z += increment;
|
||||
|
||||
// get all intersecting spanners
|
||||
QVector<SharedObjectPointer> results;
|
||||
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), expandedBounds, results);
|
||||
|
||||
// clear/voxelize as appropriate
|
||||
SharedObjectPointer heightfield;
|
||||
foreach (const SharedObjectPointer& result, results) {
|
||||
Spanner* newSpanner = static_cast<Spanner*>(result.data())->clearAndFetchHeight(bounds, heightfield);
|
||||
if (newSpanner != result) {
|
||||
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newSpanner);
|
||||
}
|
||||
}
|
||||
|
||||
// voxelize the fetched heightfield, if any
|
||||
if (heightfield) {
|
||||
VoxelMaterialSpannerEditVisitor visitor(static_cast<Spanner*>(heightfield.data()), material, color);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
||||
VoxelMaterialSpannerEditVisitor visitor(spanner, material, color);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
||||
PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& averageColor) :
|
||||
MaterialEdit(material, averageColor),
|
||||
position(position),
|
||||
radius(radius) {
|
||||
}
|
||||
|
||||
class PaintVoxelMaterialEditVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& color);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
glm::vec3 _position;
|
||||
float _radius;
|
||||
SharedObjectPointer _material;
|
||||
QColor _color;
|
||||
Box _bounds;
|
||||
};
|
||||
|
||||
PaintVoxelMaterialEditVisitor::PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& color) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
|
||||
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
|
||||
_position(position),
|
||||
_radius(radius),
|
||||
_material(material),
|
||||
_color(color) {
|
||||
|
||||
glm::vec3 extents(_radius, _radius, _radius);
|
||||
_bounds = Box(_position - extents, _position + extents);
|
||||
}
|
||||
|
||||
int PaintVoxelMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
||||
if (!info.getBounds().intersects(_bounds)) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
|
||||
VoxelMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<VoxelMaterialDataPointer>();
|
||||
if (!(colorPointer && materialPointer && colorPointer->getSize() == materialPointer->getSize())) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
QVector<QRgb> colorContents = colorPointer->getContents();
|
||||
QByteArray materialContents = materialPointer->getContents();
|
||||
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
|
||||
|
||||
Box overlap = info.getBounds().getIntersection(_bounds);
|
||||
int size = colorPointer->getSize();
|
||||
int area = size * size;
|
||||
float scale = (size - 1.0f) / info.size;
|
||||
overlap.minimum = (overlap.minimum - info.minimum) * scale;
|
||||
overlap.maximum = (overlap.maximum - info.minimum) * scale;
|
||||
int minX = glm::ceil(overlap.minimum.x);
|
||||
int minY = glm::ceil(overlap.minimum.y);
|
||||
int minZ = glm::ceil(overlap.minimum.z);
|
||||
int sizeX = (int)overlap.maximum.x - minX + 1;
|
||||
int sizeY = (int)overlap.maximum.y - minY + 1;
|
||||
int sizeZ = (int)overlap.maximum.z - minZ + 1;
|
||||
|
||||
QRgb rgb = _color.rgba();
|
||||
float step = 1.0f / scale;
|
||||
glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step);
|
||||
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
|
||||
QRgb* colorData = colorContents.data();
|
||||
uchar* materialData = (uchar*)materialContents.data();
|
||||
for (int destZ = minZ * area + minY * size + minX, endZ = destZ + sizeZ * area; destZ != endZ;
|
||||
destZ += area, position.z += step) {
|
||||
position.y = info.minimum.y + minY * step;
|
||||
for (int destY = destZ, endY = destY + sizeY * size; destY != endY; destY += size, position.y += step) {
|
||||
position.x = info.minimum.x + minX * step;
|
||||
for (int destX = destY, endX = destX + sizeX; destX != endX; destX++, position.x += step) {
|
||||
QRgb& color = colorData[destX];
|
||||
if (qAlpha(color) != 0 && glm::distance(position, _position) <= _radius) {
|
||||
color = rgb;
|
||||
materialData[destX] = materialIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, size));
|
||||
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
|
||||
encodeInline<VoxelColorDataPointer>(newColorPointer));
|
||||
|
||||
clearUnusedMaterials(materials, materialContents);
|
||||
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, size, materials));
|
||||
info.outputValues[1] = AttributeValue(_inputs.at(1), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
|
||||
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
void PaintVoxelMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||
// make sure it's 100% opaque
|
||||
QColor color = averageColor;
|
||||
color.setAlphaF(1.0f);
|
||||
PaintVoxelMaterialEditVisitor visitor(position, radius, material, color);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
|
|
@ -203,8 +203,11 @@ public:
|
|||
STREAM glm::vec3 position;
|
||||
STREAM float radius;
|
||||
STREAM float height;
|
||||
STREAM bool set;
|
||||
STREAM bool erase;
|
||||
|
||||
PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, float height = 0.0f);
|
||||
PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
|
||||
float height = 0.0f, bool set = false, bool erase = false);
|
||||
|
||||
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
|
||||
};
|
||||
|
@ -225,54 +228,39 @@ public:
|
|||
|
||||
DECLARE_STREAMABLE_METATYPE(MaterialEdit)
|
||||
|
||||
/// An edit that sets a region of a heightfield material.
|
||||
class PaintHeightfieldMaterialEdit : STREAM public MaterialEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM glm::vec3 position;
|
||||
STREAM float radius;
|
||||
|
||||
PaintHeightfieldMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
|
||||
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
|
||||
|
||||
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit)
|
||||
|
||||
/// An edit that sets the materials of voxels within a spanner to a value.
|
||||
class VoxelMaterialSpannerEdit : STREAM public MaterialEdit {
|
||||
/// An edit that sets the materials of a heightfield within a spanner to a value.
|
||||
class HeightfieldMaterialSpannerEdit : STREAM public MaterialEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM SharedObjectPointer spanner;
|
||||
STREAM bool paint;
|
||||
STREAM bool voxelize;
|
||||
|
||||
VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(),
|
||||
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
|
||||
HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(),
|
||||
const SharedObjectPointer& material = SharedObjectPointer(),
|
||||
const QColor& averageColor = QColor(), bool paint = false, bool voxelize = false);
|
||||
|
||||
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit)
|
||||
DECLARE_STREAMABLE_METATYPE(HeightfieldMaterialSpannerEdit)
|
||||
|
||||
/// An edit that sets a region of a voxel material.
|
||||
class PaintVoxelMaterialEdit : STREAM public MaterialEdit {
|
||||
/// An edit that fills a region of a heightfield height.
|
||||
class FillHeightfieldHeightEdit : public MetavoxelEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM glm::vec3 position;
|
||||
STREAM float radius;
|
||||
|
||||
PaintVoxelMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
|
||||
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
|
||||
|
||||
FillHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f);
|
||||
|
||||
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(PaintVoxelMaterialEdit)
|
||||
DECLARE_STREAMABLE_METATYPE(FillHeightfieldHeightEdit)
|
||||
|
||||
#endif // hifi_MetavoxelMessages_h
|
||||
|
|
|
@ -135,7 +135,7 @@ void SharedObject::writeExtra(Bitstream& out) const {
|
|||
// nothing by default
|
||||
}
|
||||
|
||||
void SharedObject::readExtra(Bitstream& in) {
|
||||
void SharedObject::readExtra(Bitstream& in, bool reread) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ void SharedObject::writeExtraDelta(Bitstream& out, const SharedObject* reference
|
|||
// nothing by default
|
||||
}
|
||||
|
||||
void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference) {
|
||||
void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
|
|
|
@ -84,13 +84,15 @@ public:
|
|||
virtual void writeExtra(Bitstream& out) const;
|
||||
|
||||
/// Reads the non-property contents of this object from the specified stream.
|
||||
virtual void readExtra(Bitstream& in);
|
||||
/// \param reread if true, reread the contents from the stream but don't reapply them
|
||||
virtual void readExtra(Bitstream& in, bool reread = false);
|
||||
|
||||
/// Writes the delta-encoded non-property contents of this object to the specified stream.
|
||||
virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const;
|
||||
|
||||
/// Reads the delta-encoded non-property contents of this object from the specified stream.
|
||||
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference);
|
||||
/// \param reread if true, reread the contents from the stream but don't reapply them
|
||||
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread = false);
|
||||
|
||||
/// Writes the subdivision of the contents of this object (preceeded by a
|
||||
/// reference to the object itself) to the specified stream if necessary.
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,11 +18,13 @@
|
|||
#include "MetavoxelUtil.h"
|
||||
|
||||
class AbstractHeightfieldNodeRenderer;
|
||||
class DataBlock;
|
||||
class Heightfield;
|
||||
class HeightfieldColor;
|
||||
class HeightfieldHeight;
|
||||
class HeightfieldMaterial;
|
||||
class HeightfieldNode;
|
||||
class HeightfieldStack;
|
||||
class SpannerRenderer;
|
||||
|
||||
/// An object that spans multiple octree cells.
|
||||
|
@ -71,19 +73,20 @@ public:
|
|||
/// Finds the intersection between the described ray and this spanner.
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
/// Attempts to paint on the spanner.
|
||||
/// \return the modified spanner, or this if no modification was performed
|
||||
virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material,
|
||||
const QColor& color);
|
||||
|
||||
/// Attempts to modify the spanner's height.
|
||||
/// \param set whether to set the height as opposed to raising/lowering it
|
||||
/// \param erase whether to erase height values
|
||||
/// \return the modified spanner, or this if no modification was performed
|
||||
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
|
||||
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase);
|
||||
|
||||
/// Attempts to clear and fetch part of the spanner's height.
|
||||
/// \param heightfield the heightfield to populate
|
||||
/// Attempts to fill the spanner's height (adding removing volumetric information).
|
||||
/// \return the modified spanner, or this if no modification was performed
|
||||
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
|
||||
virtual Spanner* fillHeight(const glm::vec3& position, float radius);
|
||||
|
||||
/// Attempts to "sculpt" or "paint," etc., with the supplied spanner.
|
||||
/// \return the modified spanner, or this if no modification was performed
|
||||
virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material,
|
||||
const QColor& color, bool paint, bool voxelize);
|
||||
|
||||
/// Checks whether this spanner has its own colors.
|
||||
virtual bool hasOwnColors() const;
|
||||
|
@ -292,6 +295,42 @@ private:
|
|||
QUrl _url;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
|
||||
|
||||
/// Base class for blocks of data.
|
||||
class DataBlock : public QSharedData {
|
||||
public:
|
||||
|
||||
static const int COLOR_BYTES = 3;
|
||||
|
||||
virtual ~DataBlock();
|
||||
|
||||
void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; }
|
||||
const DataBlockPointer& getDeltaData() const { return _deltaData; }
|
||||
|
||||
void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
|
||||
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
|
||||
|
||||
QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; }
|
||||
|
||||
protected:
|
||||
|
||||
QByteArray _encoded;
|
||||
QMutex _encodedMutex;
|
||||
|
||||
DataBlockPointer _deltaData;
|
||||
QByteArray _encodedDelta;
|
||||
QMutex _encodedDeltaMutex;
|
||||
|
||||
class EncodedSubdivision {
|
||||
public:
|
||||
DataBlockPointer ancestor;
|
||||
QByteArray data;
|
||||
};
|
||||
QVector<EncodedSubdivision> _encodedSubdivisions;
|
||||
QMutex _encodedSubdivisionsMutex;
|
||||
};
|
||||
|
||||
/// Base class for heightfield data blocks.
|
||||
class HeightfieldData : public DataBlock {
|
||||
public:
|
||||
|
@ -466,6 +505,126 @@ Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value);
|
|||
template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
|
||||
template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
|
||||
|
||||
/// Contains the description of a material.
|
||||
class MaterialObject : public SharedObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QUrl diffuse MEMBER _diffuse)
|
||||
Q_PROPERTY(float scaleS MEMBER _scaleS)
|
||||
Q_PROPERTY(float scaleT MEMBER _scaleT)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE MaterialObject();
|
||||
|
||||
const QUrl& getDiffuse() const { return _diffuse; }
|
||||
|
||||
float getScaleS() const { return _scaleS; }
|
||||
float getScaleT() const { return _scaleT; }
|
||||
|
||||
private:
|
||||
|
||||
QUrl _diffuse;
|
||||
float _scaleS;
|
||||
float _scaleT;
|
||||
};
|
||||
|
||||
/// Finds a material index for the supplied material in the provided list, adding an entry if necessary. Returns -1
|
||||
/// on failure (no room to add new material).
|
||||
int getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials);
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldStack> HeightfieldStackPointer;
|
||||
|
||||
/// A single column within a stack block.
|
||||
class StackArray : public QByteArray {
|
||||
public:
|
||||
|
||||
#pragma pack(push, 1)
|
||||
/// A single entry within the array.
|
||||
class Entry {
|
||||
public:
|
||||
quint32 color;
|
||||
uchar material;
|
||||
quint32 hermiteX;
|
||||
quint32 hermiteY;
|
||||
quint32 hermiteZ;
|
||||
|
||||
Entry();
|
||||
|
||||
bool isSet() const { return qAlpha(color) != 0; }
|
||||
|
||||
bool isZero() const;
|
||||
bool isMergeable(const Entry& other) const;
|
||||
|
||||
void setHermiteX(const glm::vec3& normal, float position);
|
||||
float getHermiteX(glm::vec3& normal) const;
|
||||
|
||||
void setHermiteY(const glm::vec3& normal, float position);
|
||||
float getHermiteY(glm::vec3& normal) const;
|
||||
|
||||
void setHermiteZ(const glm::vec3& normal, float position);
|
||||
float getHermiteZ(glm::vec3& normal) const;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static int getSize(int entries) { return (entries == 0) ? 0 : sizeof(quint16) + sizeof(Entry) * entries; }
|
||||
|
||||
StackArray() : QByteArray() { }
|
||||
StackArray(int entries) : QByteArray(getSize(entries), 0) { }
|
||||
StackArray(const QByteArray& other) : QByteArray(other) { }
|
||||
StackArray(const char* src, int bytes) : QByteArray(src, bytes) { }
|
||||
|
||||
int getPosition() const { return *(const quint16*)constData(); }
|
||||
void setPosition(int position) { *(quint16*)data() = position; }
|
||||
|
||||
quint16& getPositionRef() { return *(quint16*)data(); }
|
||||
|
||||
int getEntryCount() const { return isEmpty() ? 0 : (size() - sizeof(quint16)) / sizeof(Entry); }
|
||||
|
||||
Entry* getEntryData() { return (Entry*)(data() + sizeof(quint16)); }
|
||||
const Entry* getEntryData() const { return (const Entry*)(constData() + sizeof(quint16)); }
|
||||
|
||||
int getEntryAlpha(int y, float heightfieldHeight = 0.0f) const;
|
||||
|
||||
Entry& getEntry(int y, float heightfieldHeight = 0.0f);
|
||||
const Entry& getEntry(int y, float heightfieldHeight = 0.0f) const;
|
||||
|
||||
void getExtents(int& minimumY, int& maximumY) const;
|
||||
|
||||
bool hasSetEntries() const;
|
||||
|
||||
void removeEntries(int position, int count) { remove(sizeof(quint16) + position * sizeof(Entry), count * sizeof(Entry)); }
|
||||
};
|
||||
|
||||
/// A block of stack data associated with a heightfield.
|
||||
class HeightfieldStack : public HeightfieldData {
|
||||
public:
|
||||
|
||||
HeightfieldStack(int width, const QVector<StackArray>& contents, const QVector<SharedObjectPointer>& materials);
|
||||
HeightfieldStack(Bitstream& in, int bytes);
|
||||
HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference);
|
||||
|
||||
QVector<StackArray>& getContents() { return _contents; }
|
||||
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
|
||||
|
||||
void write(Bitstream& out);
|
||||
void writeDelta(Bitstream& out, const HeightfieldStackPointer& reference);
|
||||
|
||||
private:
|
||||
|
||||
void read(Bitstream& in, int bytes);
|
||||
|
||||
QVector<StackArray> _contents;
|
||||
QVector<SharedObjectPointer> _materials;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(HeightfieldStackPointer)
|
||||
|
||||
Bitstream& operator<<(Bitstream& out, const HeightfieldStackPointer& value);
|
||||
Bitstream& operator>>(Bitstream& in, HeightfieldStackPointer& value);
|
||||
|
||||
template<> void Bitstream::writeRawDelta(const HeightfieldStackPointer& value, const HeightfieldStackPointer& reference);
|
||||
template<> void Bitstream::readRawDelta(HeightfieldStackPointer& value, const HeightfieldStackPointer& reference);
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldNode> HeightfieldNodePointer;
|
||||
|
||||
/// Holds the base state used in streaming heightfield data.
|
||||
|
@ -499,14 +658,15 @@ public:
|
|||
|
||||
HeightfieldNode(const HeightfieldHeightPointer& height = HeightfieldHeightPointer(),
|
||||
const HeightfieldColorPointer& color = HeightfieldColorPointer(),
|
||||
const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer());
|
||||
const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer(),
|
||||
const HeightfieldStackPointer& stack = HeightfieldStackPointer());
|
||||
|
||||
HeightfieldNode(const HeightfieldNode& other);
|
||||
|
||||
~HeightfieldNode();
|
||||
|
||||
void setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color,
|
||||
const HeightfieldMaterialPointer& material);
|
||||
const HeightfieldMaterialPointer& material, const HeightfieldStackPointer& stack);
|
||||
|
||||
void setHeight(const HeightfieldHeightPointer& height) { _height = height; }
|
||||
const HeightfieldHeightPointer& getHeight() const { return _height; }
|
||||
|
@ -517,6 +677,9 @@ public:
|
|||
void setMaterial(const HeightfieldMaterialPointer& material) { _material = material; }
|
||||
const HeightfieldMaterialPointer& getMaterial() const { return _material; }
|
||||
|
||||
void setStack(const HeightfieldStackPointer& stack) { _stack = stack; }
|
||||
const HeightfieldStackPointer& getStack() const { return _stack; }
|
||||
|
||||
void setRenderer(AbstractHeightfieldNodeRenderer* renderer) { _renderer = renderer; }
|
||||
AbstractHeightfieldNodeRenderer* getRenderer() const { return _renderer; }
|
||||
|
||||
|
@ -525,21 +688,25 @@ public:
|
|||
void setChild(int index, const HeightfieldNodePointer& child) { _children[index] = child; }
|
||||
const HeightfieldNodePointer& getChild(int index) const { return _children[index]; }
|
||||
|
||||
float getHeight(const glm::vec3& location) const;
|
||||
bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
void getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const;
|
||||
|
||||
HeightfieldNode* paintMaterial(const glm::vec3& position, const glm::vec3& radius, const SharedObjectPointer& material,
|
||||
const QColor& color);
|
||||
|
||||
void getRangeAfterHeightPaint(const glm::vec3& position, const glm::vec3& radius,
|
||||
float height, int& minimum, int& maximum) const;
|
||||
|
||||
HeightfieldNode* paintHeight(const glm::vec3& position, const glm::vec3& radius, float height,
|
||||
HeightfieldNode* paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& position, float radius, float height, bool set, bool erase,
|
||||
float normalizeScale, float normalizeOffset);
|
||||
|
||||
HeightfieldNode* fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& position, float radius);
|
||||
|
||||
void getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const Box& editBounds, float& minimum, float& maximum) const;
|
||||
|
||||
HeightfieldNode* setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, bool voxelize,
|
||||
float normalizeScale, float normalizeOffset);
|
||||
|
||||
HeightfieldNode* clearAndFetchHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const Box& bounds, SharedObjectPointer& heightfield);
|
||||
|
||||
void read(HeightfieldStreamState& state);
|
||||
void write(HeightfieldStreamState& state) const;
|
||||
|
@ -563,9 +730,16 @@ private:
|
|||
QRgb getColorAt(const glm::vec3& location) const;
|
||||
int getMaterialAt(const glm::vec3& location) const;
|
||||
|
||||
void maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, int innerStackWidth,
|
||||
QVector<quint16>& heightContents, QVector<StackArray>& stackContents);
|
||||
|
||||
bool findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
float boundsDistance, float& distance) const;
|
||||
|
||||
HeightfieldHeightPointer _height;
|
||||
HeightfieldColorPointer _color;
|
||||
HeightfieldMaterialPointer _material;
|
||||
HeightfieldStackPointer _stack;
|
||||
|
||||
HeightfieldNodePointer _children[CHILD_COUNT];
|
||||
|
||||
|
@ -577,6 +751,9 @@ class AbstractHeightfieldNodeRenderer {
|
|||
public:
|
||||
|
||||
virtual ~AbstractHeightfieldNodeRenderer();
|
||||
|
||||
virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
|
||||
const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
|
||||
};
|
||||
|
||||
/// A heightfield represented as a spanner.
|
||||
|
@ -588,8 +765,9 @@ class Heightfield : public Transformable {
|
|||
Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged STORED false)
|
||||
Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged STORED false
|
||||
DESIGNABLE false)
|
||||
Q_PROPERTY(HeightfieldNodePointer root MEMBER _root WRITE setRoot NOTIFY rootChanged STORED false DESIGNABLE false)
|
||||
|
||||
Q_PROPERTY(HeightfieldStackPointer stack MEMBER _stack WRITE setStack NOTIFY stackChanged STORED false
|
||||
DESIGNABLE false)
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE Heightfield();
|
||||
|
@ -609,7 +787,10 @@ public:
|
|||
void setMaterial(const HeightfieldMaterialPointer& material);
|
||||
const HeightfieldMaterialPointer& getMaterial() const { return _material; }
|
||||
|
||||
void setRoot(const HeightfieldNodePointer& root);
|
||||
void setStack(const HeightfieldStackPointer& stack);
|
||||
const HeightfieldStackPointer& getStack() const { return _stack; }
|
||||
|
||||
void setRoot(const HeightfieldNodePointer& root) { _root = root; }
|
||||
const HeightfieldNodePointer& getRoot() const { return _root; }
|
||||
|
||||
MetavoxelLOD transformLOD(const MetavoxelLOD& lod) const;
|
||||
|
@ -622,13 +803,13 @@ public:
|
|||
|
||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||
|
||||
virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material,
|
||||
const QColor& color);
|
||||
|
||||
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
|
||||
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase);
|
||||
|
||||
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
|
||||
virtual Spanner* fillHeight(const glm::vec3& position, float radius);
|
||||
|
||||
virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material,
|
||||
const QColor& color, bool paint, bool voxelize);
|
||||
|
||||
virtual bool hasOwnColors() const;
|
||||
virtual bool hasOwnMaterials() const;
|
||||
virtual QRgb getColorAt(const glm::vec3& point);
|
||||
|
@ -639,9 +820,9 @@ public:
|
|||
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
|
||||
|
||||
virtual void writeExtra(Bitstream& out) const;
|
||||
virtual void readExtra(Bitstream& in);
|
||||
virtual void readExtra(Bitstream& in, bool reread);
|
||||
virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const;
|
||||
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference);
|
||||
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread);
|
||||
virtual void maybeWriteSubdivision(Bitstream& out);
|
||||
virtual SharedObject* readSubdivision(Bitstream& in);
|
||||
|
||||
|
@ -652,7 +833,7 @@ signals:
|
|||
void heightChanged(const HeightfieldHeightPointer& height);
|
||||
void colorChanged(const HeightfieldColorPointer& color);
|
||||
void materialChanged(const HeightfieldMaterialPointer& material);
|
||||
void rootChanged(const HeightfieldNodePointer& root);
|
||||
void stackChanged(const HeightfieldStackPointer& stack);
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -665,12 +846,15 @@ private slots:
|
|||
|
||||
private:
|
||||
|
||||
Heightfield* prepareEdit(float minimumValue, float maximumValue, float& normalizeScale, float& normalizeOffset);
|
||||
|
||||
float _aspectY;
|
||||
float _aspectZ;
|
||||
|
||||
HeightfieldHeightPointer _height;
|
||||
HeightfieldColorPointer _color;
|
||||
HeightfieldMaterialPointer _material;
|
||||
HeightfieldStackPointer _stack;
|
||||
|
||||
HeightfieldNodePointer _root;
|
||||
};
|
||||
|
|
|
@ -389,6 +389,8 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
|
|||
_rootPlaceName = hostname;
|
||||
_rootPlaceID = QUuid();
|
||||
|
||||
qDebug() << "Possible domain change required to connect to domain at" << hostname << "on" << port;
|
||||
|
||||
emit possibleDomainChangeRequired(hostname, port);
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
case PacketTypeAudioStreamStats:
|
||||
return 1;
|
||||
case PacketTypeMetavoxelData:
|
||||
return 10;
|
||||
return 12;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
|
|||
_entityMotionStates.insert(motionState);
|
||||
} else {
|
||||
// We failed to add the entity to the simulation. Probably because we couldn't create a shape for it.
|
||||
qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
|
||||
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
|
||||
delete motionState;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -262,7 +262,7 @@ bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direc
|
|||
return false; // origin below plane
|
||||
}
|
||||
float divisor = glm::dot(normal, direction);
|
||||
if (divisor > -EPSILON) {
|
||||
if (divisor >= 0.0f) {
|
||||
return false;
|
||||
}
|
||||
float t = dividend / divisor;
|
||||
|
@ -490,4 +490,4 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len
|
|||
vertexArrayA[i] = vertexArrayB[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue