mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 06:53:59 +02:00
Merge pull request #3583 from ey6es/metavoxels
Added ray/dual contour intersection testing, cursors on dual contour surfaces, "material brush" for dual contour surfaces.
This commit is contained in:
commit
1f09d07ac9
9 changed files with 553 additions and 17 deletions
32
interface/resources/shaders/metavoxel_voxel_cursor.frag
Normal file
32
interface/resources/shaders/metavoxel_voxel_cursor.frag
Normal file
|
@ -0,0 +1,32 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_voxel_cursor.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/10/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// the inner radius of the outline, squared
|
||||
const float SQUARED_OUTLINE_INNER_RADIUS = 0.81;
|
||||
|
||||
// the outer radius of the outline, squared
|
||||
const float SQUARED_OUTLINE_OUTER_RADIUS = 1.0;
|
||||
|
||||
// the inner radius of the inset, squared
|
||||
const float SQUARED_INSET_INNER_RADIUS = 0.855625;
|
||||
|
||||
// the outer radius of the inset, squared
|
||||
const float SQUARED_INSET_OUTER_RADIUS = 0.950625;
|
||||
|
||||
void main(void) {
|
||||
// use the distance to compute the ring color, then multiply it by the varying color
|
||||
float squaredDistance = dot(gl_TexCoord[0].stp, gl_TexCoord[0].stp);
|
||||
float alpha = step(SQUARED_OUTLINE_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_OUTLINE_OUTER_RADIUS);
|
||||
float white = step(SQUARED_INSET_INNER_RADIUS, squaredDistance) * step(squaredDistance, SQUARED_INSET_OUTER_RADIUS);
|
||||
gl_FragColor = gl_Color * vec4(white, white, white, alpha);
|
||||
}
|
25
interface/resources/shaders/metavoxel_voxel_cursor.vert
Normal file
25
interface/resources/shaders/metavoxel_voxel_cursor.vert
Normal file
|
@ -0,0 +1,25 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_voxel_cursor.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 10/10/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
void main(void) {
|
||||
// compute the view space coordinates
|
||||
vec4 viewPosition = gl_ModelViewMatrix * gl_Vertex;
|
||||
gl_Position = gl_ProjectionMatrix * viewPosition;
|
||||
|
||||
// generate the texture coordinates from the view position
|
||||
gl_TexCoord[0] = vec4(dot(viewPosition, gl_EyePlaneS[4]), dot(viewPosition, gl_EyePlaneT[4]),
|
||||
dot(viewPosition, gl_EyePlaneR[4]), 1.0);
|
||||
|
||||
// copy the color for interpolation
|
||||
gl_FrontColor = gl_Color;
|
||||
}
|
|
@ -328,6 +328,49 @@ bool MetavoxelSystem::findFirstRayHeightfieldIntersection(const glm::vec3& origi
|
|||
return true;
|
||||
}
|
||||
|
||||
class RayVoxelIntersectionVisitor : public RayIntersectionVisitor {
|
||||
public:
|
||||
|
||||
float intersectionDistance;
|
||||
|
||||
RayVoxelIntersectionVisitor(const glm::vec3& origin, const glm::vec3& direction, const MetavoxelLOD& lod);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info, float distance);
|
||||
};
|
||||
|
||||
RayVoxelIntersectionVisitor::RayVoxelIntersectionVisitor(const glm::vec3& origin,
|
||||
const glm::vec3& direction, const MetavoxelLOD& lod) :
|
||||
RayIntersectionVisitor(origin, direction, QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(), QVector<AttributePointer>(), lod),
|
||||
intersectionDistance(FLT_MAX) {
|
||||
}
|
||||
|
||||
int RayVoxelIntersectionVisitor::visit(MetavoxelInfo& info, float distance) {
|
||||
if (!info.isLeaf) {
|
||||
return _order;
|
||||
}
|
||||
const VoxelBuffer* buffer = static_cast<VoxelBuffer*>(
|
||||
info.inputValues.at(0).getInlineValue<BufferDataPointer>().data());
|
||||
if (!buffer) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
glm::vec3 entry = ((_origin + distance * _direction) - info.minimum) / info.size;
|
||||
if (buffer->findFirstRayIntersection(entry, _origin, _direction, intersectionDistance)) {
|
||||
return SHORT_CIRCUIT;
|
||||
}
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
bool MetavoxelSystem::findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) {
|
||||
RayVoxelIntersectionVisitor visitor(origin, direction, getLOD());
|
||||
guideToAugmented(visitor);
|
||||
if (visitor.intersectionDistance == FLT_MAX) {
|
||||
return false;
|
||||
}
|
||||
distance = visitor.intersectionDistance;
|
||||
return true;
|
||||
}
|
||||
|
||||
class HeightfieldHeightVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
|
@ -411,10 +454,10 @@ float MetavoxelSystem::getHeightfieldHeight(const glm::vec3& location) {
|
|||
return visitor.height;
|
||||
}
|
||||
|
||||
class HeightfieldCursorRenderVisitor : public MetavoxelVisitor {
|
||||
class CursorRenderVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
HeightfieldCursorRenderVisitor(const Box& bounds);
|
||||
CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
|
@ -423,13 +466,12 @@ private:
|
|||
Box _bounds;
|
||||
};
|
||||
|
||||
HeightfieldCursorRenderVisitor::HeightfieldCursorRenderVisitor(const Box& bounds) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute()),
|
||||
CursorRenderVisitor::CursorRenderVisitor(const AttributePointer& attribute, const Box& bounds) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << attribute),
|
||||
_bounds(bounds) {
|
||||
}
|
||||
|
||||
int HeightfieldCursorRenderVisitor::visit(MetavoxelInfo& info) {
|
||||
int CursorRenderVisitor::visit(MetavoxelInfo& info) {
|
||||
if (!info.getBounds().intersects(_bounds)) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
@ -465,7 +507,8 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r
|
|||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glm::vec3 extents(radius, radius, radius);
|
||||
HeightfieldCursorRenderVisitor visitor(Box(position - extents, position + extents));
|
||||
CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(),
|
||||
Box(position - extents, position + extents));
|
||||
guideToAugmented(visitor);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldCursorProgram().release();
|
||||
|
@ -478,6 +521,42 @@ void MetavoxelSystem::renderHeightfieldCursor(const glm::vec3& position, float r
|
|||
glDepthFunc(GL_LESS);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::renderVoxelCursor(const glm::vec3& position, float radius) {
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(-1.0f, -1.0f);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().bind();
|
||||
|
||||
glActiveTexture(GL_TEXTURE4);
|
||||
float scale = 1.0f / radius;
|
||||
glm::vec4 sCoefficients(scale, 0.0f, 0.0f, -scale * position.x);
|
||||
glm::vec4 tCoefficients(0.0f, scale, 0.0f, -scale * position.y);
|
||||
glm::vec4 rCoefficients(0.0f, 0.0f, scale, -scale * position.z);
|
||||
glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&sCoefficients);
|
||||
glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&tCoefficients);
|
||||
glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&rCoefficients);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glm::vec3 extents(radius, radius, radius);
|
||||
CursorRenderVisitor visitor(Application::getInstance()->getMetavoxels()->getVoxelBufferAttribute(),
|
||||
Box(position - extents, position + extents));
|
||||
guideToAugmented(visitor);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getVoxelCursorProgram().release();
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDepthFunc(GL_LESS);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::deleteTextures(int heightID, int colorID, int textureID) {
|
||||
glDeleteTextures(1, (GLuint*)&heightID);
|
||||
glDeleteTextures(1, (GLuint*)&colorID);
|
||||
|
@ -991,10 +1070,12 @@ void VoxelPoint::setNormal(const glm::vec3& normal) {
|
|||
}
|
||||
|
||||
VoxelBuffer::VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
|
||||
const QVector<SharedObjectPointer>& materials) :
|
||||
const QMultiHash<QRgb, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials) :
|
||||
_vertices(vertices),
|
||||
_indices(indices),
|
||||
_hermite(hermite),
|
||||
_quadIndices(quadIndices),
|
||||
_size(size),
|
||||
_vertexCount(vertices.size()),
|
||||
_indexCount(indices.size()),
|
||||
_hermiteCount(hermite.size()),
|
||||
|
@ -1002,18 +1083,107 @@ VoxelBuffer::VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>
|
|||
_materials(materials) {
|
||||
}
|
||||
|
||||
static bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance) {
|
||||
glm::vec3 firstSide = v0 - v1;
|
||||
glm::vec3 secondSide = v2 - v1;
|
||||
glm::vec3 normal = glm::cross(secondSide, firstSide);
|
||||
float dividend = glm::dot(normal, v1) - glm::dot(origin, normal);
|
||||
if (dividend > 0.0f) {
|
||||
return false; // origin below plane
|
||||
}
|
||||
float divisor = glm::dot(normal, direction);
|
||||
if (divisor > -EPSILON) {
|
||||
return false;
|
||||
}
|
||||
float t = dividend / divisor;
|
||||
glm::vec3 point = origin + direction * t;
|
||||
if (glm::dot(normal, glm::cross(point - v1, firstSide)) > 0.0f &&
|
||||
glm::dot(normal, glm::cross(secondSide, point - v1)) > 0.0f &&
|
||||
glm::dot(normal, glm::cross(point - v0, v2 - v0)) > 0.0f) {
|
||||
distance = t;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VoxelBuffer::findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin,
|
||||
const glm::vec3& direction, float& distance) const {
|
||||
float highest = _size - 1.0f;
|
||||
glm::vec3 position = entry * highest;
|
||||
glm::vec3 floors = glm::floor(position);
|
||||
int max = _size - 2;
|
||||
int x = qMin((int)floors.x, max), y = qMin((int)floors.y, max), z = qMin((int)floors.z, max);
|
||||
forever {
|
||||
for (QMultiHash<QRgb, int>::const_iterator it = _quadIndices.constFind(qRgb(x + 1, y + 1, z + 1));
|
||||
it != _quadIndices.constEnd(); it++) {
|
||||
const int* indices = _indices.constData() + *it;
|
||||
if (findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex,
|
||||
_vertices.at(indices[1]).vertex, _vertices.at(indices[2]).vertex, distance) ||
|
||||
findRayTriangleIntersection(origin, direction, _vertices.at(indices[0]).vertex,
|
||||
_vertices.at(indices[2]).vertex, _vertices.at(indices[3]).vertex, distance)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
float xDistance = FLT_MAX, yDistance = FLT_MAX, zDistance = FLT_MAX;
|
||||
if (direction.x > 0.0f) {
|
||||
xDistance = (x + 1.0f - position.x) / direction.x;
|
||||
} else if (direction.x < 0.0f) {
|
||||
xDistance = (x - position.x) / direction.x;
|
||||
}
|
||||
if (direction.y > 0.0f) {
|
||||
yDistance = (y + 1.0f - position.y) / direction.y;
|
||||
} else if (direction.y < 0.0f) {
|
||||
yDistance = (y - position.y) / direction.y;
|
||||
}
|
||||
if (direction.z > 0.0f) {
|
||||
zDistance = (z + 1.0f - position.z) / direction.z;
|
||||
} else if (direction.z < 0.0f) {
|
||||
zDistance = (z - position.z) / direction.z;
|
||||
}
|
||||
float minimumDistance = qMin(xDistance, qMin(yDistance, zDistance));
|
||||
if (minimumDistance == xDistance) {
|
||||
if (direction.x > 0.0f) {
|
||||
if (x++ == max) {
|
||||
return false;
|
||||
}
|
||||
} else if (x-- == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (minimumDistance == yDistance) {
|
||||
if (direction.y > 0.0f) {
|
||||
if (y++ == max) {
|
||||
return false;
|
||||
}
|
||||
} else if (y-- == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (minimumDistance == zDistance) {
|
||||
if (direction.z > 0.0f) {
|
||||
if (z++ == max) {
|
||||
return false;
|
||||
}
|
||||
} else if (z-- == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
position += direction * minimumDistance;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VoxelBuffer::render(bool cursor) {
|
||||
if (!_vertexBuffer.isCreated()) {
|
||||
_vertexBuffer.create();
|
||||
_vertexBuffer.bind();
|
||||
_vertexBuffer.allocate(_vertices.constData(), _vertices.size() * sizeof(VoxelPoint));
|
||||
_vertices.clear();
|
||||
|
||||
|
||||
_indexBuffer.create();
|
||||
_indexBuffer.bind();
|
||||
_indexBuffer.allocate(_indices.constData(), _indices.size() * sizeof(int));
|
||||
_indices.clear();
|
||||
|
||||
|
||||
if (!_materials.isEmpty()) {
|
||||
_networkTextures.resize(_materials.size());
|
||||
for (int i = 0; i < _materials.size(); i++) {
|
||||
|
@ -1036,7 +1206,7 @@ void VoxelBuffer::render(bool cursor) {
|
|||
|
||||
glDrawRangeElements(GL_QUADS, 0, _vertexCount - 1, _indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
if (!_materials.isEmpty()) {
|
||||
if (!(_materials.isEmpty() || cursor)) {
|
||||
Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false);
|
||||
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
@ -1118,7 +1288,7 @@ void VoxelBuffer::render(bool cursor) {
|
|||
_vertexBuffer.release();
|
||||
_indexBuffer.release();
|
||||
|
||||
if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData)) {
|
||||
if (_hermiteCount > 0 && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHermiteData) && !cursor) {
|
||||
if (!_hermiteBuffer.isCreated()) {
|
||||
_hermiteBuffer.create();
|
||||
_hermiteBuffer.bind();
|
||||
|
@ -1214,6 +1384,12 @@ void DefaultMetavoxelRendererImplementation::init() {
|
|||
_baseVoxelProgram.link();
|
||||
|
||||
loadSplatProgram("voxel", _splatVoxelProgram, _splatVoxelLocations);
|
||||
|
||||
_voxelCursorProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_voxel_cursor.vert");
|
||||
_voxelCursorProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_voxel_cursor.frag");
|
||||
_voxelCursorProgram.link();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1627,6 +1803,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
QVector<VoxelPoint> vertices;
|
||||
QVector<int> indices;
|
||||
QVector<glm::vec3> hermiteSegments;
|
||||
QMultiHash<QRgb, int> quadIndices;
|
||||
|
||||
// see http://www.frankpetterson.com/publications/dualcontour/dualcontour.pdf for a description of the
|
||||
// dual contour algorithm for generating meshes from voxel data using Hermite-tagged edges
|
||||
|
@ -2091,6 +2268,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
// quads for each edge that includes a transition, using indices of previously generated vertices
|
||||
if (x != 0 && y != 0 && z != 0) {
|
||||
if (alpha0 != alpha1) {
|
||||
quadIndices.insert(qRgb(x, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x, y - 1, z), indices.size());
|
||||
quadIndices.insert(qRgb(x, y - 1, z - 1), indices.size());
|
||||
quadIndices.insert(qRgb(x, y, z - 1), indices.size());
|
||||
indices.append(index.x);
|
||||
int index1 = lastLineIndices.at(x).x;
|
||||
int index2 = lastPlaneIndices.at((y - 1) * expanded + x).x;
|
||||
|
@ -2107,6 +2288,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
|
||||
if (alpha0 != alpha2) {
|
||||
quadIndices.insert(qRgb(x, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x - 1, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x - 1, y, z - 1), indices.size());
|
||||
quadIndices.insert(qRgb(x, y, z - 1), indices.size());
|
||||
indices.append(index.y);
|
||||
int index1 = lastIndex.y;
|
||||
int index2 = lastPlaneIndices.at(y * expanded + x - 1).y;
|
||||
|
@ -2123,6 +2308,10 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
}
|
||||
|
||||
if (alpha0 != alpha4) {
|
||||
quadIndices.insert(qRgb(x, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x - 1, y, z), indices.size());
|
||||
quadIndices.insert(qRgb(x - 1, y - 1, z), indices.size());
|
||||
quadIndices.insert(qRgb(x, y - 1, z), indices.size());
|
||||
indices.append(index.z);
|
||||
int index1 = lastIndex.z;
|
||||
int index2 = lastLineIndices.at(x - 1).z;
|
||||
|
@ -2158,7 +2347,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
colorZ += area;
|
||||
}
|
||||
}
|
||||
buffer = new VoxelBuffer(vertices, indices, hermiteSegments,
|
||||
buffer = new VoxelBuffer(vertices, indices, hermiteSegments, quadIndices, size,
|
||||
material ? material->getMaterials() : QVector<SharedObjectPointer>());
|
||||
}
|
||||
BufferDataPointer pointer(buffer);
|
||||
|
@ -2430,6 +2619,7 @@ ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldCursorProgram;
|
|||
ProgramObject DefaultMetavoxelRendererImplementation::_baseVoxelProgram;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_splatVoxelProgram;
|
||||
DefaultMetavoxelRendererImplementation::SplatLocations DefaultMetavoxelRendererImplementation::_splatVoxelLocations;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_voxelCursorProgram;
|
||||
|
||||
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
|
||||
GLdouble coefficients[] = { x, y, z, w };
|
||||
|
|
|
@ -46,8 +46,12 @@ public:
|
|||
|
||||
void renderHeightfieldCursor(const glm::vec3& position, float radius);
|
||||
|
||||
void renderVoxelCursor(const glm::vec3& position, float radius);
|
||||
|
||||
bool findFirstRayHeightfieldIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
|
||||
|
||||
bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
|
||||
|
||||
Q_INVOKABLE float getHeightfieldHeight(const glm::vec3& location);
|
||||
|
||||
Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID);
|
||||
|
@ -241,7 +245,13 @@ class VoxelBuffer : public BufferData {
|
|||
public:
|
||||
|
||||
VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
|
||||
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
|
||||
const QMultiHash<QRgb, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
|
||||
QVector<SharedObjectPointer>());
|
||||
|
||||
/// 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;
|
||||
|
||||
virtual void render(bool cursor = false);
|
||||
|
||||
|
@ -250,6 +260,8 @@ private:
|
|||
QVector<VoxelPoint> _vertices;
|
||||
QVector<int> _indices;
|
||||
QVector<glm::vec3> _hermite;
|
||||
QMultiHash<QRgb, int> _quadIndices;
|
||||
int _size;
|
||||
int _vertexCount;
|
||||
int _indexCount;
|
||||
int _hermiteCount;
|
||||
|
@ -311,6 +323,8 @@ public:
|
|||
static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; }
|
||||
static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; }
|
||||
|
||||
static ProgramObject& getVoxelCursorProgram() { return _voxelCursorProgram; }
|
||||
|
||||
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
||||
|
||||
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
||||
|
@ -344,6 +358,8 @@ private:
|
|||
static ProgramObject _baseVoxelProgram;
|
||||
static ProgramObject _splatVoxelProgram;
|
||||
static SplatLocations _splatVoxelLocations;
|
||||
|
||||
static ProgramObject _voxelCursorProgram;
|
||||
};
|
||||
|
||||
/// Base class for spanner renderers; provides clipping.
|
||||
|
|
|
@ -129,6 +129,7 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
addTool(new EraseHeightfieldTool(this));
|
||||
addTool(new VoxelMaterialBoxTool(this));
|
||||
addTool(new VoxelMaterialSpannerTool(this));
|
||||
addTool(new VoxelMaterialBrushTool(this));
|
||||
|
||||
updateAttributes();
|
||||
|
||||
|
@ -1525,3 +1526,102 @@ void VoxelMaterialSpannerTool::updateTexture() {
|
|||
void VoxelMaterialSpannerTool::textureLoaded() {
|
||||
_color->setColor(_texture->getAverageColor());
|
||||
}
|
||||
|
||||
VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) :
|
||||
MetavoxelTool(editor, name, false, true) {
|
||||
|
||||
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 distance;
|
||||
if (!Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, distance)) {
|
||||
return;
|
||||
}
|
||||
Application::getInstance()->getMetavoxels()->renderVoxelCursor(
|
||||
_position = origin + distance * 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) {
|
||||
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") {
|
||||
|
||||
_form->addRow("Color:", _color = new QColorEditor(this));
|
||||
connect(_color, &QColorEditor::colorChanged, this, &VoxelMaterialBrushTool::clearTexture);
|
||||
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
|
||||
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialBrushTool::updateTexture);
|
||||
}
|
||||
|
||||
QVariant VoxelMaterialBrushTool::createEdit(bool alternate) {
|
||||
if (alternate) {
|
||||
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
|
||||
} else {
|
||||
SharedObjectPointer material = _materialEditor->getObject();
|
||||
if (static_cast<MaterialObject*>(material.data())->getDiffuse().isValid()) {
|
||||
_materialEditor->detachObject();
|
||||
} else {
|
||||
material = SharedObjectPointer();
|
||||
}
|
||||
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), material, _color->getColor()));
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialBrushTool::clearTexture() {
|
||||
_materialEditor->setObject(new MaterialObject());
|
||||
}
|
||||
|
||||
void VoxelMaterialBrushTool::updateTexture() {
|
||||
if (_texture) {
|
||||
_texture->disconnect(this);
|
||||
}
|
||||
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
|
||||
if (!material->getDiffuse().isValid()) {
|
||||
_texture.clear();
|
||||
return;
|
||||
}
|
||||
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
|
||||
if (_texture) {
|
||||
if (_texture->isLoaded()) {
|
||||
textureLoaded();
|
||||
} else {
|
||||
connect(_texture.data(), &Resource::loaded, this, &VoxelMaterialBrushTool::textureLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelMaterialBrushTool::textureLoaded() {
|
||||
_color->setColor(_texture->getAverageColor());
|
||||
}
|
||||
|
|
|
@ -469,4 +469,53 @@ private:
|
|||
QSharedPointer<NetworkTexture> _texture;
|
||||
};
|
||||
|
||||
/// 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;
|
||||
};
|
||||
|
||||
/// Allows texturing parts of the voxel field.
|
||||
class VoxelMaterialBrushTool : public VoxelBrushTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
VoxelMaterialBrushTool(MetavoxelEditor* editor);
|
||||
|
||||
protected:
|
||||
|
||||
virtual QVariant createEdit(bool alternate);
|
||||
|
||||
private slots:
|
||||
|
||||
void clearTexture();
|
||||
void updateTexture();
|
||||
void textureLoaded();
|
||||
|
||||
private:
|
||||
|
||||
QColorEditor* _color;
|
||||
SharedObjectEditor* _materialEditor;
|
||||
QSharedPointer<NetworkTexture> _texture;
|
||||
};
|
||||
|
||||
#endif // hifi_MetavoxelEditor_h
|
||||
|
|
|
@ -848,3 +848,108 @@ void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObject
|
|||
VoxelMaterialSpannerEditVisitor visitor(spanner, material, averageColor);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
||||
PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius,
|
||||
const SharedObjectPointer& material, const QColor& averageColor) :
|
||||
position(position),
|
||||
radius(radius),
|
||||
material(material),
|
||||
averageColor(averageColor) {
|
||||
}
|
||||
|
||||
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 {
|
||||
PaintVoxelMaterialEditVisitor visitor(position, radius, material, averageColor);
|
||||
data.guide(visitor);
|
||||
}
|
||||
|
|
|
@ -261,4 +261,23 @@ public:
|
|||
|
||||
DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit)
|
||||
|
||||
/// An edit that sets a region of a voxel material.
|
||||
class PaintVoxelMaterialEdit : public MetavoxelEdit {
|
||||
STREAMABLE
|
||||
|
||||
public:
|
||||
|
||||
STREAM glm::vec3 position;
|
||||
STREAM float radius;
|
||||
STREAM SharedObjectPointer material;
|
||||
STREAM QColor averageColor;
|
||||
|
||||
PaintVoxelMaterialEdit(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(PaintVoxelMaterialEdit)
|
||||
|
||||
#endif // hifi_MetavoxelMessages_h
|
||||
|
|
|
@ -85,7 +85,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
case PacketTypeAudioStreamStats:
|
||||
return 1;
|
||||
case PacketTypeMetavoxelData:
|
||||
return 5;
|
||||
return 6;
|
||||
case PacketTypeVoxelData:
|
||||
return VERSION_VOXELS_HAS_FILE_BREAKS;
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue