mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into domain-server-auth
This commit is contained in:
commit
5b18d42940
14 changed files with 1102 additions and 180 deletions
25
interface/resources/shaders/metavoxel_heightfield.frag
Normal file
25
interface/resources/shaders/metavoxel_heightfield.frag
Normal file
|
@ -0,0 +1,25 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_heightfield.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 7/28/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 diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
void main(void) {
|
||||
// compute the base color based on OpenGL lighting model
|
||||
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
|
||||
gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalize(normal), gl_LightSource[0].position)));
|
||||
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st);
|
||||
}
|
33
interface/resources/shaders/metavoxel_heightfield.vert
Normal file
33
interface/resources/shaders/metavoxel_heightfield.vert
Normal file
|
@ -0,0 +1,33 @@
|
|||
#version 120
|
||||
|
||||
//
|
||||
// metavoxel_heighfield.vert
|
||||
// vertex shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 7/28/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 height texture
|
||||
uniform sampler2D heightMap;
|
||||
|
||||
// the interpolated normal
|
||||
varying vec4 normal;
|
||||
|
||||
void main(void) {
|
||||
// transform and store the normal for interpolation
|
||||
normal = normalize(gl_ModelViewMatrix * vec4(0.0, 1.0, 0.0, 0.0));
|
||||
|
||||
// pass along the texture coordinates
|
||||
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||
|
||||
// add the height to the position
|
||||
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
|
||||
|
||||
// the zero height should be invisible
|
||||
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
|
||||
}
|
|
@ -25,7 +25,7 @@
|
|||
#include "MetavoxelSystem.h"
|
||||
#include "renderer/Model.h"
|
||||
|
||||
REGISTER_META_OBJECT(PointMetavoxelRendererImplementation)
|
||||
REGISTER_META_OBJECT(DefaultMetavoxelRendererImplementation)
|
||||
REGISTER_META_OBJECT(SphereRenderer)
|
||||
REGISTER_META_OBJECT(StaticModelRenderer)
|
||||
|
||||
|
@ -33,8 +33,10 @@ static int bufferPointVectorMetaTypeId = qRegisterMetaType<BufferPointVector>();
|
|||
|
||||
void MetavoxelSystem::init() {
|
||||
MetavoxelClientManager::init();
|
||||
PointMetavoxelRendererImplementation::init();
|
||||
_pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new PointBufferAttribute());
|
||||
DefaultMetavoxelRendererImplementation::init();
|
||||
_pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new BufferDataAttribute("pointBuffer"));
|
||||
_heightfieldBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(
|
||||
new BufferDataAttribute("heightfieldBuffer"));
|
||||
}
|
||||
|
||||
MetavoxelLOD MetavoxelSystem::getLOD() {
|
||||
|
@ -42,28 +44,31 @@ MetavoxelLOD MetavoxelSystem::getLOD() {
|
|||
return _lod;
|
||||
}
|
||||
|
||||
class SpannerSimulateVisitor : public SpannerVisitor {
|
||||
class SimulateVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
SpannerSimulateVisitor(float deltaTime);
|
||||
SimulateVisitor(float deltaTime, const MetavoxelLOD& lod);
|
||||
|
||||
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
float _deltaTime;
|
||||
};
|
||||
|
||||
SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime) :
|
||||
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||
QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(),
|
||||
Application::getInstance()->getMetavoxels()->getLOD()),
|
||||
SimulateVisitor::SimulateVisitor(float deltaTime, const MetavoxelLOD& lod) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getRendererAttribute(),
|
||||
QVector<AttributePointer>(), lod),
|
||||
_deltaTime(deltaTime) {
|
||||
}
|
||||
|
||||
bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
|
||||
spanner->getRenderer()->simulate(_deltaTime);
|
||||
return true;
|
||||
int SimulateVisitor::visit(MetavoxelInfo& info) {
|
||||
if (!info.isLeaf) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
static_cast<MetavoxelRenderer*>(info.inputValues.at(0).getInlineValue<
|
||||
SharedObjectPointer>().data())->getImplementation()->simulate(*_data, _deltaTime, info, _lod);
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
void MetavoxelSystem::simulate(float deltaTime) {
|
||||
|
@ -76,28 +81,8 @@ void MetavoxelSystem::simulate(float deltaTime) {
|
|||
BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier());
|
||||
}
|
||||
|
||||
SpannerSimulateVisitor spannerSimulateVisitor(deltaTime);
|
||||
guide(spannerSimulateVisitor);
|
||||
}
|
||||
|
||||
class SpannerRenderVisitor : public SpannerVisitor {
|
||||
public:
|
||||
|
||||
SpannerRenderVisitor();
|
||||
|
||||
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
|
||||
};
|
||||
|
||||
SpannerRenderVisitor::SpannerRenderVisitor() :
|
||||
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||
QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(),
|
||||
Application::getInstance()->getMetavoxels()->getLOD(),
|
||||
encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) {
|
||||
}
|
||||
|
||||
bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
|
||||
spanner->getRenderer()->render(1.0f, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize);
|
||||
return true;
|
||||
SimulateVisitor simulateVisitor(deltaTime, getLOD());
|
||||
guideToAugmented(simulateVisitor);
|
||||
}
|
||||
|
||||
class RenderVisitor : public MetavoxelVisitor {
|
||||
|
@ -125,9 +110,11 @@ int RenderVisitor::visit(MetavoxelInfo& info) {
|
|||
void MetavoxelSystem::render() {
|
||||
RenderVisitor renderVisitor(getLOD());
|
||||
guideToAugmented(renderVisitor);
|
||||
|
||||
SpannerRenderVisitor spannerRenderVisitor;
|
||||
guide(spannerRenderVisitor);
|
||||
}
|
||||
|
||||
void MetavoxelSystem::deleteTextures(int heightID, int colorID) {
|
||||
glDeleteTextures(1, (GLuint*)&heightID);
|
||||
glDeleteTextures(1, (GLuint*)&colorID);
|
||||
}
|
||||
|
||||
MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) {
|
||||
|
@ -239,6 +226,9 @@ void MetavoxelSystemClient::sendDatagram(const QByteArray& data) {
|
|||
Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size());
|
||||
}
|
||||
|
||||
BufferData::~BufferData() {
|
||||
}
|
||||
|
||||
PointBuffer::PointBuffer(const BufferPointVector& points) :
|
||||
_points(points) {
|
||||
}
|
||||
|
@ -269,34 +259,214 @@ void PointBuffer::render() {
|
|||
_buffer.release();
|
||||
}
|
||||
|
||||
PointBufferAttribute::PointBufferAttribute() :
|
||||
InlineAttribute<PointBufferPointer>("pointBuffer") {
|
||||
HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
||||
const QByteArray& height, const QByteArray& color, bool clearAfterLoading) :
|
||||
_translation(translation),
|
||||
_scale(scale),
|
||||
_height(height),
|
||||
_color(color),
|
||||
_clearAfterLoading(clearAfterLoading),
|
||||
_heightTextureID(0),
|
||||
_colorTextureID(0),
|
||||
_heightSize(glm::sqrt(height.size())) {
|
||||
}
|
||||
|
||||
bool PointBufferAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
PointBufferPointer firstChild = decodeInline<PointBufferPointer>(children[0]);
|
||||
HeightfieldBuffer::~HeightfieldBuffer() {
|
||||
// the textures have to be deleted on the main thread (for its opengl context)
|
||||
if (QThread::currentThread() != Application::getInstance()->thread()) {
|
||||
QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "deleteTextures",
|
||||
Q_ARG(int, _heightTextureID), Q_ARG(int, _colorTextureID));
|
||||
} else {
|
||||
glDeleteTextures(1, &_heightTextureID);
|
||||
glDeleteTextures(1, &_colorTextureID);
|
||||
}
|
||||
}
|
||||
|
||||
class HeightfieldPoint {
|
||||
public:
|
||||
glm::vec2 textureCoord;
|
||||
glm::vec3 vertex;
|
||||
};
|
||||
|
||||
void HeightfieldBuffer::render() {
|
||||
// initialize textures, etc. on first render
|
||||
if (_heightTextureID == 0) {
|
||||
glGenTextures(1, &_heightTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, _height.constData());
|
||||
if (_clearAfterLoading) {
|
||||
_height.clear();
|
||||
}
|
||||
|
||||
glGenTextures(1, &_colorTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
if (_color.isEmpty()) {
|
||||
const quint8 WHITE_COLOR[] = { 255, 255, 255 };
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, WHITE_COLOR);
|
||||
|
||||
} else {
|
||||
int colorSize = glm::sqrt(_color.size() / 3);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colorSize, colorSize, 0, GL_RGB, GL_UNSIGNED_BYTE, _color.constData());
|
||||
if (_clearAfterLoading) {
|
||||
_color.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
// create the buffer objects lazily
|
||||
int sizeWithSkirt = _heightSize + 2;
|
||||
int vertexCount = sizeWithSkirt * sizeWithSkirt;
|
||||
int rows = sizeWithSkirt - 1;
|
||||
int indexCount = rows * rows * 4;
|
||||
BufferPair& bufferPair = _bufferPairs[_heightSize];
|
||||
if (!bufferPair.first.isCreated()) {
|
||||
QVector<HeightfieldPoint> vertices(vertexCount);
|
||||
HeightfieldPoint* point = vertices.data();
|
||||
|
||||
float step = 1.0f / (_heightSize - 1);
|
||||
float z = -step;
|
||||
for (int i = 0; i < sizeWithSkirt; i++, z += step) {
|
||||
float x = -step;
|
||||
const float SKIRT_LENGTH = 0.25f;
|
||||
float baseY = (i == 0 || i == sizeWithSkirt - 1) ? -SKIRT_LENGTH : 0.0f;
|
||||
for (int j = 0; j < sizeWithSkirt; j++, point++, x += step) {
|
||||
point->vertex = glm::vec3(x, (j == 0 || j == sizeWithSkirt - 1) ? -SKIRT_LENGTH : baseY, z);
|
||||
point->textureCoord = glm::vec2(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
bufferPair.first.setUsagePattern(QOpenGLBuffer::StaticDraw);
|
||||
bufferPair.first.create();
|
||||
bufferPair.first.bind();
|
||||
bufferPair.first.allocate(vertices.constData(), vertexCount * sizeof(HeightfieldPoint));
|
||||
|
||||
QVector<int> indices(indexCount);
|
||||
int* index = indices.data();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
int lineIndex = i * sizeWithSkirt;
|
||||
int nextLineIndex = (i + 1) * sizeWithSkirt;
|
||||
for (int j = 0; j < rows; j++) {
|
||||
*index++ = lineIndex + j;
|
||||
*index++ = lineIndex + j + 1;
|
||||
*index++ = nextLineIndex + j + 1;
|
||||
*index++ = nextLineIndex + j;
|
||||
}
|
||||
}
|
||||
|
||||
bufferPair.second = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
|
||||
bufferPair.second.create();
|
||||
bufferPair.second.bind();
|
||||
bufferPair.second.allocate(indices.constData(), indexCount * sizeof(int));
|
||||
|
||||
} else {
|
||||
bufferPair.first.bind();
|
||||
bufferPair.second.bind();
|
||||
}
|
||||
|
||||
HeightfieldPoint* point = 0;
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(HeightfieldPoint), &point->vertex);
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(HeightfieldPoint), &point->textureCoord);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(_translation.x, _translation.y, _translation.z);
|
||||
glScalef(_scale, _scale, _scale);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||
|
||||
glDrawRangeElements(GL_QUADS, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
bufferPair.first.release();
|
||||
bufferPair.second.release();
|
||||
}
|
||||
|
||||
QHash<int, HeightfieldBuffer::BufferPair> HeightfieldBuffer::_bufferPairs;
|
||||
|
||||
void HeightfieldPreview::render(const glm::vec3& translation, float scale) const {
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_EQUAL, 0.0f);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().bind();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(translation.x, translation.y, translation.z);
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
foreach (const BufferDataPointer& buffer, _buffers) {
|
||||
buffer->render();
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getHeightfieldProgram().release();
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
BufferDataAttribute::BufferDataAttribute(const QString& name) :
|
||||
InlineAttribute<BufferDataPointer>(name) {
|
||||
}
|
||||
|
||||
bool BufferDataAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
BufferDataPointer firstChild = decodeInline<BufferDataPointer>(children[0]);
|
||||
for (int i = 1; i < MERGE_COUNT; i++) {
|
||||
if (firstChild != decodeInline<PointBufferPointer>(children[i])) {
|
||||
*(PointBufferPointer*)&parent = _defaultValue;
|
||||
if (firstChild != decodeInline<BufferDataPointer>(children[i])) {
|
||||
*(BufferDataPointer*)&parent = _defaultValue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*(PointBufferPointer*)&parent = firstChild;
|
||||
*(BufferDataPointer*)&parent = firstChild;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PointMetavoxelRendererImplementation::init() {
|
||||
if (!_program.isLinked()) {
|
||||
_program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert");
|
||||
_program.link();
|
||||
void DefaultMetavoxelRendererImplementation::init() {
|
||||
if (!_pointProgram.isLinked()) {
|
||||
_pointProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert");
|
||||
_pointProgram.link();
|
||||
|
||||
_program.bind();
|
||||
_pointScaleLocation = _program.uniformLocation("pointScale");
|
||||
_program.release();
|
||||
_pointProgram.bind();
|
||||
_pointScaleLocation = _pointProgram.uniformLocation("pointScale");
|
||||
_pointProgram.release();
|
||||
|
||||
_heightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield.vert");
|
||||
_heightfieldProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() +
|
||||
"shaders/metavoxel_heightfield.frag");
|
||||
_heightfieldProgram.link();
|
||||
|
||||
_heightfieldProgram.bind();
|
||||
_heightfieldProgram.setUniformValue("heightMap", 0);
|
||||
_heightfieldProgram.setUniformValue("diffuseMap", 1);
|
||||
_heightfieldProgram.release();
|
||||
}
|
||||
}
|
||||
|
||||
PointMetavoxelRendererImplementation::PointMetavoxelRendererImplementation() {
|
||||
DefaultMetavoxelRendererImplementation::DefaultMetavoxelRendererImplementation() {
|
||||
}
|
||||
|
||||
class PointAugmentVisitor : public MetavoxelVisitor {
|
||||
|
@ -341,11 +511,14 @@ int PointAugmentVisitor::visit(MetavoxelInfo& info) {
|
|||
{ quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } };
|
||||
_points.append(point);
|
||||
}
|
||||
if (info.size >= _pointLeafSize) {
|
||||
BufferPointVector swapPoints;
|
||||
_points.swap(swapPoints);
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(PointBufferPointer(
|
||||
new PointBuffer(swapPoints))));
|
||||
if (info.size >= _pointLeafSize) {
|
||||
PointBuffer* buffer = NULL;
|
||||
if (!_points.isEmpty()) {
|
||||
BufferPointVector swapPoints;
|
||||
_points.swap(swapPoints);
|
||||
buffer = new PointBuffer(swapPoints);
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
||||
}
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
@ -354,14 +527,46 @@ bool PointAugmentVisitor::postVisit(MetavoxelInfo& info) {
|
|||
if (info.size != _pointLeafSize) {
|
||||
return false;
|
||||
}
|
||||
BufferPointVector swapPoints;
|
||||
_points.swap(swapPoints);
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(PointBufferPointer(
|
||||
new PointBuffer(swapPoints))));
|
||||
PointBuffer* buffer = NULL;
|
||||
if (!_points.isEmpty()) {
|
||||
BufferPointVector swapPoints;
|
||||
_points.swap(swapPoints);
|
||||
buffer = new PointBuffer(swapPoints);
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void PointMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous,
|
||||
class HeightfieldAugmentVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
HeightfieldAugmentVisitor(const MetavoxelLOD& lod);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
};
|
||||
|
||||
HeightfieldAugmentVisitor::HeightfieldAugmentVisitor(const MetavoxelLOD& lod) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>() <<
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(), lod) {
|
||||
}
|
||||
|
||||
int HeightfieldAugmentVisitor::visit(MetavoxelInfo& info) {
|
||||
if (info.isLeaf) {
|
||||
HeightfieldBuffer* buffer = NULL;
|
||||
HeightfieldDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldDataPointer>();
|
||||
if (height) {
|
||||
HeightfieldDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldDataPointer>();
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, height->getContents(),
|
||||
color ? color->getContents() : QByteArray());
|
||||
}
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(BufferDataPointer(buffer)));
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
|
||||
void DefaultMetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous,
|
||||
MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
||||
// copy the previous buffers
|
||||
MetavoxelData expandedPrevious = previous;
|
||||
|
@ -374,15 +579,73 @@ void PointMetavoxelRendererImplementation::augment(MetavoxelData& data, const Me
|
|||
data.setRoot(pointBufferAttribute, root);
|
||||
root->incrementReferenceCount();
|
||||
}
|
||||
const AttributePointer& heightfieldBufferAttribute =
|
||||
Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute();
|
||||
root = expandedPrevious.getRoot(heightfieldBufferAttribute);
|
||||
if (root) {
|
||||
data.setRoot(heightfieldBufferAttribute, root);
|
||||
root->incrementReferenceCount();
|
||||
}
|
||||
|
||||
PointAugmentVisitor visitor(lod);
|
||||
data.guideToDifferent(expandedPrevious, visitor);
|
||||
PointAugmentVisitor pointAugmentVisitor(lod);
|
||||
data.guideToDifferent(expandedPrevious, pointAugmentVisitor);
|
||||
|
||||
HeightfieldAugmentVisitor heightfieldAugmentVisitor(lod);
|
||||
data.guideToDifferent(expandedPrevious, heightfieldAugmentVisitor);
|
||||
}
|
||||
|
||||
class PointRenderVisitor : public MetavoxelVisitor {
|
||||
class SpannerSimulateVisitor : public SpannerVisitor {
|
||||
public:
|
||||
|
||||
PointRenderVisitor(const MetavoxelLOD& lod);
|
||||
SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod);
|
||||
|
||||
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
|
||||
|
||||
private:
|
||||
|
||||
float _deltaTime;
|
||||
};
|
||||
|
||||
SpannerSimulateVisitor::SpannerSimulateVisitor(float deltaTime, const MetavoxelLOD& lod) :
|
||||
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||
QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(), lod),
|
||||
_deltaTime(deltaTime) {
|
||||
}
|
||||
|
||||
bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
|
||||
spanner->getRenderer()->simulate(_deltaTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DefaultMetavoxelRendererImplementation::simulate(MetavoxelData& data, float deltaTime,
|
||||
MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
||||
SpannerSimulateVisitor spannerSimulateVisitor(deltaTime, lod);
|
||||
data.guide(spannerSimulateVisitor);
|
||||
}
|
||||
|
||||
class SpannerRenderVisitor : public SpannerVisitor {
|
||||
public:
|
||||
|
||||
SpannerRenderVisitor(const MetavoxelLOD& lod);
|
||||
|
||||
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
|
||||
};
|
||||
|
||||
SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) :
|
||||
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||
QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(),
|
||||
lod, encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) {
|
||||
}
|
||||
|
||||
bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
|
||||
spanner->getRenderer()->render(1.0f, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
class BufferRenderVisitor : public MetavoxelVisitor {
|
||||
public:
|
||||
|
||||
BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
|
||||
|
@ -391,21 +654,23 @@ private:
|
|||
int _order;
|
||||
};
|
||||
|
||||
PointRenderVisitor::PointRenderVisitor(const MetavoxelLOD& lod) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << Application::getInstance()->getMetavoxels()->getPointBufferAttribute(),
|
||||
QVector<AttributePointer>(), lod),
|
||||
BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << attribute, QVector<AttributePointer>(), lod),
|
||||
_order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) {
|
||||
}
|
||||
|
||||
int PointRenderVisitor::visit(MetavoxelInfo& info) {
|
||||
PointBufferPointer buffer = info.inputValues.at(0).getInlineValue<PointBufferPointer>();
|
||||
int BufferRenderVisitor::visit(MetavoxelInfo& info) {
|
||||
BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>();
|
||||
if (buffer) {
|
||||
buffer->render();
|
||||
}
|
||||
return info.isLeaf ? STOP_RECURSION : _order;
|
||||
}
|
||||
|
||||
void PointMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
||||
void DefaultMetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
||||
SpannerRenderVisitor spannerRenderVisitor(lod);
|
||||
data.guide(spannerRenderVisitor);
|
||||
|
||||
int viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
const int VIEWPORT_WIDTH_INDEX = 2;
|
||||
|
@ -416,8 +681,8 @@ void PointMetavoxelRendererImplementation::render(MetavoxelData& data, Metavoxel
|
|||
float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(),
|
||||
Application::getInstance()->getViewFrustum()->getNearTopRight());
|
||||
|
||||
_program.bind();
|
||||
_program.setUniformValue(_pointScaleLocation, viewportDiagonal *
|
||||
_pointProgram.bind();
|
||||
_pointProgram.setUniformValue(_pointScaleLocation, viewportDiagonal *
|
||||
Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
@ -428,22 +693,41 @@ void PointMetavoxelRendererImplementation::render(MetavoxelData& data, Metavoxel
|
|||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
PointRenderVisitor visitor(lod);
|
||||
data.guide(visitor);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
BufferRenderVisitor pointRenderVisitor(Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod);
|
||||
data.guide(pointRenderVisitor);
|
||||
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
_program.release();
|
||||
_pointProgram.release();
|
||||
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_EQUAL, 0.0f);
|
||||
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
_heightfieldProgram.bind();
|
||||
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
BufferRenderVisitor heightfieldRenderVisitor(Application::getInstance()->getMetavoxels()->getHeightfieldBufferAttribute(),
|
||||
lod);
|
||||
data.guide(heightfieldRenderVisitor);
|
||||
|
||||
_heightfieldProgram.release();
|
||||
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
|
||||
ProgramObject PointMetavoxelRendererImplementation::_program;
|
||||
int PointMetavoxelRendererImplementation::_pointScaleLocation;
|
||||
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_pointProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_pointScaleLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_heightfieldProgram;
|
||||
|
||||
static void enableClipPlane(GLenum plane, float x, float y, float z, float w) {
|
||||
GLdouble coefficients[] = { x, y, z, w };
|
||||
|
|
|
@ -36,10 +36,13 @@ public:
|
|||
virtual MetavoxelLOD getLOD();
|
||||
|
||||
const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; }
|
||||
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
|
||||
|
||||
void simulate(float deltaTime);
|
||||
void render();
|
||||
|
||||
Q_INVOKABLE void deleteTextures(int heightID, int colorID);
|
||||
|
||||
protected:
|
||||
|
||||
virtual MetavoxelClient* createClient(const SharedNodePointer& node);
|
||||
|
@ -49,6 +52,7 @@ private:
|
|||
void guideToAugmented(MetavoxelVisitor& visitor);
|
||||
|
||||
AttributePointer _pointBufferAttribute;
|
||||
AttributePointer _heightfieldBufferAttribute;
|
||||
|
||||
MetavoxelLOD _lod;
|
||||
QReadWriteLock _lodLock;
|
||||
|
@ -92,13 +96,24 @@ private:
|
|||
QReadWriteLock _augmentedDataLock;
|
||||
};
|
||||
|
||||
/// Base class for cached static buffers.
|
||||
class BufferData : public QSharedData {
|
||||
public:
|
||||
|
||||
virtual ~BufferData();
|
||||
|
||||
virtual void render() = 0;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer;
|
||||
|
||||
/// Contains the information necessary to render a group of points.
|
||||
class PointBuffer : public QSharedData {
|
||||
class PointBuffer : public BufferData {
|
||||
public:
|
||||
|
||||
PointBuffer(const BufferPointVector& points);
|
||||
|
||||
void render();
|
||||
virtual void render();
|
||||
|
||||
private:
|
||||
|
||||
|
@ -107,36 +122,85 @@ private:
|
|||
int _pointCount;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<PointBuffer> PointBufferPointer;
|
||||
/// Contains the information necessary to render a heightfield block.
|
||||
class HeightfieldBuffer : public BufferData {
|
||||
public:
|
||||
|
||||
/// Creates a new heightfield buffer.
|
||||
/// \param clearAfterLoading if true, clear the data arrays after we load them into textures in order to reclaim the space
|
||||
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height, const QByteArray& color,
|
||||
bool clearAfterLoading = true);
|
||||
~HeightfieldBuffer();
|
||||
|
||||
const glm::vec3& getTranslation() const { return _translation; }
|
||||
|
||||
const QByteArray& getHeight() const { return _height; }
|
||||
const QByteArray& getColor() const { return _color; }
|
||||
|
||||
virtual void render();
|
||||
|
||||
/// A client-side attribute that stores point buffers.
|
||||
class PointBufferAttribute : public InlineAttribute<PointBufferPointer> {
|
||||
private:
|
||||
|
||||
glm::vec3 _translation;
|
||||
float _scale;
|
||||
QByteArray _height;
|
||||
QByteArray _color;
|
||||
bool _clearAfterLoading;
|
||||
GLuint _heightTextureID;
|
||||
GLuint _colorTextureID;
|
||||
int _heightSize;
|
||||
|
||||
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
||||
static QHash<int, BufferPair> _bufferPairs;
|
||||
};
|
||||
|
||||
/// Convenience class for rendering a preview of a heightfield.
|
||||
class HeightfieldPreview {
|
||||
public:
|
||||
|
||||
void setBuffers(const QVector<BufferDataPointer>& buffers) { _buffers = buffers; }
|
||||
const QVector<BufferDataPointer>& getBuffers() const { return _buffers; }
|
||||
|
||||
void render(const glm::vec3& translation, float scale) const;
|
||||
|
||||
private:
|
||||
|
||||
QVector<BufferDataPointer> _buffers;
|
||||
};
|
||||
|
||||
/// A client-side attribute that stores renderable buffers.
|
||||
class BufferDataAttribute : public InlineAttribute<BufferDataPointer> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE PointBufferAttribute();
|
||||
Q_INVOKABLE BufferDataAttribute(const QString& name = QString());
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
};
|
||||
|
||||
/// Renders metavoxels as points.
|
||||
class PointMetavoxelRendererImplementation : public MetavoxelRendererImplementation {
|
||||
class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplementation {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
static void init();
|
||||
|
||||
Q_INVOKABLE PointMetavoxelRendererImplementation();
|
||||
|
||||
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
|
||||
static ProgramObject _program;
|
||||
static int _pointScaleLocation;
|
||||
static ProgramObject _pointProgram;
|
||||
static int _pointScaleLocation;
|
||||
|
||||
static ProgramObject _heightfieldProgram;
|
||||
};
|
||||
|
||||
/// Base class for spanner renderers; provides clipping.
|
||||
|
|
|
@ -15,11 +15,12 @@
|
|||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QMetaProperty>
|
||||
#include <QMessageBox>
|
||||
#include <QMetaProperty>
|
||||
#include <QOpenGLFramebufferObject>
|
||||
#include <QPushButton>
|
||||
|
@ -30,6 +31,7 @@
|
|||
|
||||
#include <AttributeRegistry.h>
|
||||
#include <MetavoxelMessages.h>
|
||||
#include <MetavoxelUtil.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MetavoxelEditor.h"
|
||||
|
@ -113,6 +115,7 @@ MetavoxelEditor::MetavoxelEditor() :
|
|||
addTool(new RemoveSpannerTool(this));
|
||||
addTool(new ClearSpannersTool(this));
|
||||
addTool(new SetSpannerTool(this));
|
||||
addTool(new ImportHeightfieldTool(this));
|
||||
|
||||
updateAttributes();
|
||||
|
||||
|
@ -891,3 +894,127 @@ void SetSpannerTool::applyEdit(const AttributePointer& attribute, const SharedOb
|
|||
QThreadPool::globalInstance()->start(new Voxelizer(size, cellBounds,
|
||||
spannerData->getVoxelizationGranularity(), directionImages));
|
||||
}
|
||||
|
||||
ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
|
||||
MetavoxelTool(editor, "Import Heightfield", false) {
|
||||
|
||||
QWidget* widget = new QWidget();
|
||||
QFormLayout* form = new QFormLayout();
|
||||
widget->setLayout(form);
|
||||
layout()->addWidget(widget);
|
||||
|
||||
form->addRow("Translation:", _translation = new Vec3Editor(widget));
|
||||
form->addRow("Scale:", _scale = new QDoubleSpinBox());
|
||||
_scale->setMinimum(-FLT_MAX);
|
||||
_scale->setMaximum(FLT_MAX);
|
||||
_scale->setPrefix("2^");
|
||||
_scale->setValue(3.0);
|
||||
form->addRow("Height:", _height = new QPushButton());
|
||||
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
|
||||
form->addRow("Color:", _color = new QPushButton());
|
||||
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
|
||||
|
||||
QPushButton* applyButton = new QPushButton("Apply");
|
||||
layout()->addWidget(applyButton);
|
||||
connect(applyButton, &QAbstractButton::clicked, this, &ImportHeightfieldTool::apply);
|
||||
}
|
||||
|
||||
bool ImportHeightfieldTool::appliesTo(const AttributePointer& attribute) const {
|
||||
return attribute->inherits("HeightfieldAttribute");
|
||||
}
|
||||
|
||||
void ImportHeightfieldTool::render() {
|
||||
float scale = pow(2.0, _scale->value());
|
||||
_translation->setSingleStep(scale);
|
||||
glm::vec3 quantizedTranslation = scale * glm::floor(_translation->getValue() / scale);
|
||||
_translation->setValue(quantizedTranslation);
|
||||
_preview.render(quantizedTranslation, scale);
|
||||
}
|
||||
|
||||
void ImportHeightfieldTool::selectHeightFile() {
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Select Height Image", QString(), "Images (*.png *.jpg)");
|
||||
if (filename.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!_heightImage.load(filename)) {
|
||||
QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");
|
||||
return;
|
||||
}
|
||||
_heightImage = _heightImage.convertToFormat(QImage::Format_RGB888);
|
||||
_height->setText(filename);
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void ImportHeightfieldTool::selectColorFile() {
|
||||
QString filename = QFileDialog::getOpenFileName(this, "Select Color Image", QString(), "Images (*.png *.jpg)");
|
||||
if (filename.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!_colorImage.load(filename)) {
|
||||
QMessageBox::warning(this, "Invalid Image", "The selected image could not be read.");
|
||||
return;
|
||||
}
|
||||
_colorImage = _colorImage.convertToFormat(QImage::Format_RGB888);
|
||||
_color->setText(filename);
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void ImportHeightfieldTool::apply() {
|
||||
float scale = pow(2.0, _scale->value());
|
||||
foreach (const BufferDataPointer& bufferData, _preview.getBuffers()) {
|
||||
HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data());
|
||||
MetavoxelData data;
|
||||
data.setSize(scale);
|
||||
HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getHeight()));
|
||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
|
||||
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
|
||||
if (!buffer->getColor().isEmpty()) {
|
||||
HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getColor()));
|
||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
||||
}
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
|
||||
_translation->getValue() + buffer->getTranslation() * scale, data)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
}
|
||||
|
||||
const int BLOCK_SIZE = 32;
|
||||
const int BLOCK_ADVANCEMENT = BLOCK_SIZE - 1;
|
||||
|
||||
void ImportHeightfieldTool::updatePreview() {
|
||||
QVector<BufferDataPointer> buffers;
|
||||
if (_heightImage.width() > 0 && _heightImage.height() > 0) {
|
||||
float z = 0.0f;
|
||||
for (int i = 0; i < _heightImage.height(); i += BLOCK_ADVANCEMENT, z++) {
|
||||
float x = 0.0f;
|
||||
for (int j = 0; j < _heightImage.width(); j += BLOCK_ADVANCEMENT, x++) {
|
||||
QByteArray height(BLOCK_SIZE * BLOCK_SIZE, 0);
|
||||
int rows = qMin(BLOCK_SIZE, _heightImage.height() - i);
|
||||
int columns = qMin(BLOCK_SIZE, _heightImage.width() - j);
|
||||
const int BYTES_PER_COLOR = 3;
|
||||
for (int y = 0; y < rows; y++) {
|
||||
uchar* src = _heightImage.scanLine(i + y) + j * BYTES_PER_COLOR;
|
||||
char* dest = height.data() + y * BLOCK_SIZE;
|
||||
for (int x = 0; x < columns; x++) {
|
||||
*dest++ = *src;
|
||||
src += BYTES_PER_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray color;
|
||||
if (!_colorImage.isNull()) {
|
||||
color = QByteArray(BLOCK_SIZE * BLOCK_SIZE * BYTES_PER_COLOR, 0);
|
||||
rows = qMax(0, qMin(BLOCK_SIZE, _colorImage.height() - i));
|
||||
columns = qMax(0, qMin(BLOCK_SIZE, _colorImage.width() - j));
|
||||
for (int y = 0; y < rows; y++) {
|
||||
memcpy(color.data() + y * BLOCK_SIZE * BYTES_PER_COLOR,
|
||||
_colorImage.scanLine(i + y) + j * BYTES_PER_COLOR, columns * BYTES_PER_COLOR);
|
||||
}
|
||||
}
|
||||
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color, false)));
|
||||
}
|
||||
}
|
||||
}
|
||||
_preview.setBuffers(buffers);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QList>
|
||||
#include <QWidget>
|
||||
|
||||
#include "MetavoxelSystem.h"
|
||||
#include "renderer/ProgramObject.h"
|
||||
|
||||
class QComboBox;
|
||||
|
@ -25,6 +26,7 @@ class QPushButton;
|
|||
class QScrollArea;
|
||||
|
||||
class MetavoxelTool;
|
||||
class Vec3Editor;
|
||||
|
||||
/// Allows editing metavoxels.
|
||||
class MetavoxelEditor : public QWidget {
|
||||
|
@ -223,4 +225,37 @@ protected:
|
|||
virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
|
||||
};
|
||||
|
||||
/// Allows importing a heightfield.
|
||||
class ImportHeightfieldTool : public MetavoxelTool {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ImportHeightfieldTool(MetavoxelEditor* editor);
|
||||
|
||||
virtual bool appliesTo(const AttributePointer& attribute) const;
|
||||
|
||||
virtual void render();
|
||||
|
||||
private slots:
|
||||
|
||||
void selectHeightFile();
|
||||
void selectColorFile();
|
||||
void apply();
|
||||
|
||||
private:
|
||||
|
||||
void updatePreview();
|
||||
|
||||
Vec3Editor* _translation;
|
||||
QDoubleSpinBox* _scale;
|
||||
QPushButton* _height;
|
||||
QPushButton* _color;
|
||||
|
||||
QImage _heightImage;
|
||||
QImage _colorImage;
|
||||
|
||||
HeightfieldPreview _preview;
|
||||
};
|
||||
|
||||
#endif // hifi_MetavoxelEditor_h
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QMutexLocker>
|
||||
#include <QReadLocker>
|
||||
#include <QScriptEngine>
|
||||
#include <QWriteLocker>
|
||||
|
@ -21,6 +23,8 @@ REGISTER_META_OBJECT(QRgbAttribute)
|
|||
REGISTER_META_OBJECT(PackedNormalAttribute)
|
||||
REGISTER_META_OBJECT(SpannerQRgbAttribute)
|
||||
REGISTER_META_OBJECT(SpannerPackedNormalAttribute)
|
||||
REGISTER_META_OBJECT(HeightfieldAttribute)
|
||||
REGISTER_META_OBJECT(HeightfieldColorAttribute)
|
||||
REGISTER_META_OBJECT(SharedObjectAttribute)
|
||||
REGISTER_META_OBJECT(SharedObjectSetAttribute)
|
||||
REGISTER_META_OBJECT(SpannerSetAttribute)
|
||||
|
@ -37,17 +41,23 @@ AttributeRegistry::AttributeRegistry() :
|
|||
_guideAttribute(registerAttribute(new SharedObjectAttribute("guide", &MetavoxelGuide::staticMetaObject,
|
||||
new DefaultMetavoxelGuide()))),
|
||||
_rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject,
|
||||
new PointMetavoxelRenderer()))),
|
||||
new DefaultMetavoxelRenderer()))),
|
||||
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))),
|
||||
_colorAttribute(registerAttribute(new QRgbAttribute("color"))),
|
||||
_normalAttribute(registerAttribute(new PackedNormalAttribute("normal"))),
|
||||
_spannerColorAttribute(registerAttribute(new SpannerQRgbAttribute("spannerColor"))),
|
||||
_spannerNormalAttribute(registerAttribute(new SpannerPackedNormalAttribute("spannerNormal"))),
|
||||
_spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))) {
|
||||
_spannerMaskAttribute(registerAttribute(new FloatAttribute("spannerMask"))),
|
||||
_heightfieldAttribute(registerAttribute(new HeightfieldAttribute("heightfield"))),
|
||||
_heightfieldColorAttribute(registerAttribute(new HeightfieldColorAttribute("heightfieldColor"))) {
|
||||
|
||||
// our baseline LOD threshold is for voxels; spanners are a different story
|
||||
// our baseline LOD threshold is for voxels; spanners and heightfields are a different story
|
||||
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f;
|
||||
_spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER);
|
||||
|
||||
const float HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER = 32.0f;
|
||||
_heightfieldAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
|
||||
_heightfieldColorAttribute->setLODThresholdMultiplier(HEIGHTFIELD_LOD_THRESHOLD_MULTIPLIER);
|
||||
}
|
||||
|
||||
static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) {
|
||||
|
@ -451,6 +461,221 @@ AttributeValue SpannerPackedNormalAttribute::inherit(const AttributeValue& paren
|
|||
return AttributeValue(parentValue.getAttribute());
|
||||
}
|
||||
|
||||
HeightfieldData::HeightfieldData(const QByteArray& contents) :
|
||||
_contents(contents) {
|
||||
}
|
||||
|
||||
const int BYTES_PER_PIXEL = 3;
|
||||
|
||||
HeightfieldData::HeightfieldData(Bitstream& in, int bytes, bool color) :
|
||||
_encoded(in.readAligned(bytes)) {
|
||||
|
||||
QImage image = QImage::fromData(_encoded).convertToFormat(QImage::Format_RGB888);
|
||||
if (color) {
|
||||
_contents.resize(image.width() * image.height() * BYTES_PER_PIXEL);
|
||||
memcpy(_contents.data(), image.constBits(), _contents.size());
|
||||
|
||||
} else {
|
||||
_contents.resize(image.width() * image.height());
|
||||
char* dest = _contents.data();
|
||||
for (const uchar* src = image.constBits(), *end = src + _contents.size() * BYTES_PER_PIXEL;
|
||||
src != end; src += BYTES_PER_PIXEL) {
|
||||
*dest++ = *src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldData::write(Bitstream& out, bool color) {
|
||||
QMutexLocker locker(&_encodedMutex);
|
||||
if (_encoded.isEmpty()) {
|
||||
QImage image;
|
||||
if (color) {
|
||||
int size = glm::sqrt(_contents.size() / (float)BYTES_PER_PIXEL);
|
||||
image = QImage((uchar*)_contents.data(), size, size, QImage::Format_RGB888);
|
||||
} else {
|
||||
int size = glm::sqrt((float)_contents.size());
|
||||
image = QImage(size, size, QImage::Format_RGB888);
|
||||
uchar* dest = image.bits();
|
||||
for (const char* src = _contents.constData(), *end = src + _contents.size(); src != end; src++) {
|
||||
*dest++ = *src;
|
||||
*dest++ = *src;
|
||||
*dest++ = *src;
|
||||
}
|
||||
}
|
||||
QBuffer buffer(&_encoded);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
image.save(&buffer, "JPG");
|
||||
}
|
||||
out << _encoded.size();
|
||||
out.writeAligned(_encoded);
|
||||
}
|
||||
|
||||
HeightfieldAttribute::HeightfieldAttribute(const QString& name) :
|
||||
InlineAttribute<HeightfieldDataPointer>(name) {
|
||||
}
|
||||
|
||||
void HeightfieldAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||
} else {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||
if (data) {
|
||||
data->write(out, false);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
int maxSize = 0;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
HeightfieldDataPointer pointer = decodeInline<HeightfieldDataPointer>(children[i]);
|
||||
if (pointer) {
|
||||
maxSize = qMax(maxSize, pointer->getContents().size());
|
||||
}
|
||||
}
|
||||
if (maxSize == 0) {
|
||||
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer();
|
||||
return true;
|
||||
}
|
||||
int size = glm::sqrt((float)maxSize);
|
||||
QByteArray contents(size * size, 0);
|
||||
int halfSize = size / 2;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]);
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
const QByteArray& childContents = child->getContents();
|
||||
int childSize = glm::sqrt((float)childContents.size());
|
||||
if (childSize != size) {
|
||||
continue; // TODO: handle differently-sized children
|
||||
}
|
||||
const int INDEX_MASK = 1;
|
||||
int xIndex = i & INDEX_MASK;
|
||||
const int Y_SHIFT = 1;
|
||||
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
|
||||
if (yIndex == 0 && decodeInline<HeightfieldDataPointer>(children[i | (1 << Y_SHIFT)])) {
|
||||
continue; // bottom is overriden by top
|
||||
}
|
||||
const int HALF_RANGE = 128;
|
||||
int yOffset = yIndex * HALF_RANGE;
|
||||
int Z_SHIFT = 2;
|
||||
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
|
||||
char* dest = contents.data() + (zIndex * halfSize * size) + (xIndex * halfSize);
|
||||
uchar* src = (uchar*)childContents.data();
|
||||
int childSizePlusOne = childSize + 1;
|
||||
for (int z = 0; z < halfSize; z++) {
|
||||
for (char* end = dest + halfSize; dest != end; src += 2) {
|
||||
int max = qMax(qMax(src[0], src[1]), qMax(src[childSize], src[childSizePlusOne]));
|
||||
*dest++ = (max == 0) ? 0 : (yOffset + (max >> 1));
|
||||
}
|
||||
dest += halfSize;
|
||||
src += childSize;
|
||||
}
|
||||
}
|
||||
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents));
|
||||
return false;
|
||||
}
|
||||
|
||||
HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) :
|
||||
InlineAttribute<HeightfieldDataPointer>(name) {
|
||||
}
|
||||
|
||||
void HeightfieldColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
int size;
|
||||
in >> size;
|
||||
if (size == 0) {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer();
|
||||
} else {
|
||||
*(HeightfieldDataPointer*)&value = HeightfieldDataPointer(new HeightfieldData(in, size, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
|
||||
if (isLeaf) {
|
||||
HeightfieldDataPointer data = decodeInline<HeightfieldDataPointer>(value);
|
||||
if (data) {
|
||||
data->write(out, true);
|
||||
} else {
|
||||
out << 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool postRead) const {
|
||||
int maxSize = 0;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
HeightfieldDataPointer pointer = decodeInline<HeightfieldDataPointer>(children[i]);
|
||||
if (pointer) {
|
||||
maxSize = qMax(maxSize, pointer->getContents().size());
|
||||
}
|
||||
}
|
||||
if (maxSize == 0) {
|
||||
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer();
|
||||
return true;
|
||||
}
|
||||
int size = glm::sqrt(maxSize / (float)BYTES_PER_PIXEL);
|
||||
QByteArray contents(size * size * BYTES_PER_PIXEL, 0);
|
||||
int halfSize = size / 2;
|
||||
for (int i = 0; i < MERGE_COUNT; i++) {
|
||||
HeightfieldDataPointer child = decodeInline<HeightfieldDataPointer>(children[i]);
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
const QByteArray& childContents = child->getContents();
|
||||
int childSize = glm::sqrt(childContents.size() / (float)BYTES_PER_PIXEL);
|
||||
if (childSize != size) {
|
||||
continue; // TODO: handle differently-sized children
|
||||
}
|
||||
const int INDEX_MASK = 1;
|
||||
int xIndex = i & INDEX_MASK;
|
||||
const int Y_SHIFT = 1;
|
||||
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
|
||||
if (yIndex == 0 && decodeInline<HeightfieldDataPointer>(children[i | (1 << Y_SHIFT)])) {
|
||||
continue; // bottom is overriden by top
|
||||
}
|
||||
int Z_SHIFT = 2;
|
||||
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
|
||||
char* dest = contents.data() + ((zIndex * halfSize * size) + (xIndex * halfSize)) * BYTES_PER_PIXEL;
|
||||
uchar* src = (uchar*)childContents.data();
|
||||
int childStride = childSize * BYTES_PER_PIXEL;
|
||||
int halfStride = halfSize * BYTES_PER_PIXEL;
|
||||
int childStep = 2 * BYTES_PER_PIXEL;
|
||||
int redOffset3 = childStride + BYTES_PER_PIXEL;
|
||||
int greenOffset1 = BYTES_PER_PIXEL + 1;
|
||||
int greenOffset2 = childStride + 1;
|
||||
int greenOffset3 = childStride + BYTES_PER_PIXEL + 1;
|
||||
int blueOffset1 = BYTES_PER_PIXEL + 2;
|
||||
int blueOffset2 = childStride + 2;
|
||||
int blueOffset3 = childStride + BYTES_PER_PIXEL + 2;
|
||||
for (int z = 0; z < halfSize; z++) {
|
||||
for (char* end = dest + halfSize * BYTES_PER_PIXEL; dest != end; src += childStep) {
|
||||
*dest++ = ((int)src[0] + (int)src[BYTES_PER_PIXEL] + (int)src[childStride] + (int)src[redOffset3]) >> 2;
|
||||
*dest++ = ((int)src[1] + (int)src[greenOffset1] + (int)src[greenOffset2] + (int)src[greenOffset3]) >> 2;
|
||||
*dest++ = ((int)src[2] + (int)src[blueOffset1] + (int)src[blueOffset2] + (int)src[blueOffset3]) >> 2;
|
||||
}
|
||||
dest += halfStride;
|
||||
src += childStride;
|
||||
}
|
||||
}
|
||||
*(HeightfieldDataPointer*)&parent = HeightfieldDataPointer(new HeightfieldData(contents));
|
||||
return false;
|
||||
}
|
||||
|
||||
SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject,
|
||||
const SharedObjectPointer& defaultValue) :
|
||||
InlineAttribute<SharedObjectPointer>(name, defaultValue),
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define hifi_AttributeRegistry_h
|
||||
|
||||
#include <QHash>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QReadWriteLock>
|
||||
#include <QSharedPointer>
|
||||
|
@ -94,6 +95,12 @@ public:
|
|||
/// Returns a reference to the standard "spannerMask" attribute.
|
||||
const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; }
|
||||
|
||||
/// Returns a reference to the standard HeightfieldPointer "heightfield" attribute.
|
||||
const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; }
|
||||
|
||||
/// Returns a reference to the standard HeightfieldColorPointer "heightfieldColor" attribute.
|
||||
const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; }
|
||||
|
||||
private:
|
||||
|
||||
static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine);
|
||||
|
@ -109,6 +116,8 @@ private:
|
|||
AttributePointer _spannerColorAttribute;
|
||||
AttributePointer _spannerNormalAttribute;
|
||||
AttributePointer _spannerMaskAttribute;
|
||||
AttributePointer _heightfieldAttribute;
|
||||
AttributePointer _heightfieldColorAttribute;
|
||||
};
|
||||
|
||||
/// Converts a value to a void pointer.
|
||||
|
@ -408,6 +417,54 @@ public:
|
|||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||
};
|
||||
|
||||
/// Contains a block of heightfield data.
|
||||
class HeightfieldData : public QSharedData {
|
||||
public:
|
||||
|
||||
HeightfieldData(const QByteArray& contents);
|
||||
HeightfieldData(Bitstream& in, int bytes, bool color);
|
||||
|
||||
const QByteArray& getContents() const { return _contents; }
|
||||
|
||||
void write(Bitstream& out, bool color);
|
||||
|
||||
private:
|
||||
|
||||
QByteArray _contents;
|
||||
QByteArray _encoded;
|
||||
QMutex _encodedMutex;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
|
||||
|
||||
/// An attribute that stores heightfield data.
|
||||
class HeightfieldAttribute : public InlineAttribute<HeightfieldDataPointer> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE HeightfieldAttribute(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 bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
};
|
||||
|
||||
/// An attribute that stores heightfield colors.
|
||||
class HeightfieldColorAttribute : public InlineAttribute<HeightfieldDataPointer> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE HeightfieldColorAttribute(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 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
|
||||
|
|
|
@ -639,6 +639,16 @@ void Bitstream::readRawDelta(QScriptValue& value, const QScriptValue& reference)
|
|||
}
|
||||
}
|
||||
|
||||
void Bitstream::writeAligned(const QByteArray& data) {
|
||||
flush();
|
||||
_underlying.device()->write(data);
|
||||
}
|
||||
|
||||
QByteArray Bitstream::readAligned(int bytes) {
|
||||
reset();
|
||||
return _underlying.device()->read(bytes);
|
||||
}
|
||||
|
||||
Bitstream& Bitstream::operator<<(bool value) {
|
||||
if (value) {
|
||||
_byte |= (1 << _position);
|
||||
|
|
|
@ -430,6 +430,11 @@ public:
|
|||
template<class K, class V> void writeRawDelta(const QHash<K, V>& value, const QHash<K, V>& reference);
|
||||
template<class K, class V> void readRawDelta(QHash<K, V>& value, const QHash<K, V>& reference);
|
||||
|
||||
/// Writes the specified array aligned on byte boundaries to avoid the inefficiency
|
||||
/// of bit-twiddling (at the cost of up to seven bits of wasted space).
|
||||
void writeAligned(const QByteArray& data);
|
||||
QByteArray readAligned(int bytes);
|
||||
|
||||
Bitstream& operator<<(bool value);
|
||||
Bitstream& operator>>(bool& value);
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ REGISTER_META_OBJECT(DefaultMetavoxelGuide)
|
|||
REGISTER_META_OBJECT(ScriptedMetavoxelGuide)
|
||||
REGISTER_META_OBJECT(ThrobbingMetavoxelGuide)
|
||||
REGISTER_META_OBJECT(MetavoxelRenderer)
|
||||
REGISTER_META_OBJECT(PointMetavoxelRenderer)
|
||||
REGISTER_META_OBJECT(DefaultMetavoxelRenderer)
|
||||
REGISTER_META_OBJECT(Spanner)
|
||||
REGISTER_META_OBJECT(Sphere)
|
||||
REGISTER_META_OBJECT(StaticModel)
|
||||
|
@ -54,6 +54,18 @@ bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec3& minimum, float size,
|
||||
const MetavoxelLOD& reference, float multiplier) const {
|
||||
if (position == reference.position && threshold == reference.threshold) {
|
||||
return false; // first off, nothing becomes subdivided or collapsed if it doesn't change
|
||||
}
|
||||
if (!(shouldSubdivide(minimum, size, multiplier) || reference.shouldSubdivide(minimum, size, multiplier))) {
|
||||
return false; // this one or the reference must be subdivided
|
||||
}
|
||||
// TODO: find some way of culling subtrees that can't possibly contain subdivided or collapsed nodes
|
||||
return true;
|
||||
}
|
||||
|
||||
MetavoxelData::MetavoxelData() : _size(1.0f) {
|
||||
}
|
||||
|
||||
|
@ -567,53 +579,68 @@ void MetavoxelData::readDelta(const MetavoxelData& reference, const MetavoxelLOD
|
|||
// shallow copy the reference
|
||||
*this = reference;
|
||||
|
||||
QHash<AttributePointer, MetavoxelNode*> remainingRoots = _roots;
|
||||
|
||||
bool changed;
|
||||
in >> changed;
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool sizeChanged;
|
||||
in >> sizeChanged;
|
||||
if (sizeChanged) {
|
||||
float size;
|
||||
in >> size;
|
||||
while (_size < size) {
|
||||
expand();
|
||||
}
|
||||
}
|
||||
|
||||
forever {
|
||||
AttributePointer attribute;
|
||||
in >> attribute;
|
||||
if (!attribute) {
|
||||
break;
|
||||
}
|
||||
MetavoxelStreamBase base = { attribute, in, lod, referenceLOD };
|
||||
MetavoxelStreamState state = { base, getMinimum(), _size };
|
||||
MetavoxelNode* oldRoot = _roots.value(attribute);
|
||||
if (oldRoot) {
|
||||
bool changed;
|
||||
in >> changed;
|
||||
if (changed) {
|
||||
oldRoot->incrementReferenceCount();
|
||||
attribute->readMetavoxelDelta(*this, *oldRoot, state);
|
||||
oldRoot->decrementReferenceCount(attribute);
|
||||
} else {
|
||||
attribute->readMetavoxelSubdivision(*this, state);
|
||||
if (changed) {
|
||||
bool sizeChanged;
|
||||
in >> sizeChanged;
|
||||
if (sizeChanged) {
|
||||
float size;
|
||||
in >> size;
|
||||
while (_size < size) {
|
||||
expand();
|
||||
}
|
||||
} else {
|
||||
attribute->readMetavoxelRoot(*this, state);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 minimum = getMinimum();
|
||||
forever {
|
||||
AttributePointer attribute;
|
||||
in >> attribute;
|
||||
if (!attribute) {
|
||||
break;
|
||||
}
|
||||
MetavoxelStreamBase base = { attribute, in, lod, referenceLOD };
|
||||
MetavoxelStreamState state = { base, minimum, _size };
|
||||
MetavoxelNode* oldRoot = _roots.value(attribute);
|
||||
if (oldRoot) {
|
||||
bool changed;
|
||||
in >> changed;
|
||||
if (changed) {
|
||||
oldRoot->incrementReferenceCount();
|
||||
attribute->readMetavoxelDelta(*this, *oldRoot, state);
|
||||
oldRoot->decrementReferenceCount(attribute);
|
||||
} else {
|
||||
attribute->readMetavoxelSubdivision(*this, state);
|
||||
}
|
||||
remainingRoots.remove(attribute);
|
||||
|
||||
} else {
|
||||
attribute->readMetavoxelRoot(*this, state);
|
||||
}
|
||||
}
|
||||
|
||||
forever {
|
||||
AttributePointer attribute;
|
||||
in >> attribute;
|
||||
if (!attribute) {
|
||||
break;
|
||||
}
|
||||
_roots.take(attribute)->decrementReferenceCount(attribute);
|
||||
remainingRoots.remove(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
forever {
|
||||
AttributePointer attribute;
|
||||
in >> attribute;
|
||||
if (!attribute) {
|
||||
break;
|
||||
// read subdivisions for the remaining roots if there's any chance of a collapse
|
||||
if (!(lod.position == referenceLOD.position && lod.threshold <= referenceLOD.threshold)) {
|
||||
glm::vec3 minimum = getMinimum();
|
||||
for (QHash<AttributePointer, MetavoxelNode*>::const_iterator it = remainingRoots.constBegin();
|
||||
it != remainingRoots.constEnd(); it++) {
|
||||
MetavoxelStreamBase base = { it.key(), in, lod, referenceLOD };
|
||||
MetavoxelStreamState state = { base, minimum, _size };
|
||||
it.key()->readMetavoxelSubdivision(*this, state);
|
||||
}
|
||||
_roots.take(attribute)->decrementReferenceCount(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -788,6 +815,10 @@ bool MetavoxelStreamState::becameSubdivided() const {
|
|||
return base.lod.becameSubdivided(minimum, size, base.referenceLOD, base.attribute->getLODThresholdMultiplier());
|
||||
}
|
||||
|
||||
bool MetavoxelStreamState::becameSubdividedOrCollapsed() const {
|
||||
return base.lod.becameSubdividedOrCollapsed(minimum, size, base.referenceLOD, base.attribute->getLODThresholdMultiplier());
|
||||
}
|
||||
|
||||
void MetavoxelStreamState::setMinimum(const glm::vec3& lastMinimum, int index) {
|
||||
minimum = getNextMinimum(lastMinimum, size, index);
|
||||
}
|
||||
|
@ -923,7 +954,7 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta
|
|||
_children[i] = new MetavoxelNode(state.base.attribute);
|
||||
_children[i]->readDelta(*reference._children[i], nextState);
|
||||
} else {
|
||||
if (nextState.becameSubdivided()) {
|
||||
if (nextState.becameSubdividedOrCollapsed()) {
|
||||
_children[i] = reference._children[i]->readSubdivision(nextState);
|
||||
if (_children[i] == reference._children[i]) {
|
||||
_children[i]->incrementReferenceCount();
|
||||
|
@ -972,42 +1003,46 @@ void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamSt
|
|||
}
|
||||
|
||||
MetavoxelNode* MetavoxelNode::readSubdivision(MetavoxelStreamState& state) {
|
||||
if (!state.shouldSubdivideReference()) {
|
||||
bool leaf;
|
||||
state.base.stream >> leaf;
|
||||
if (leaf) {
|
||||
return isLeaf() ? this : new MetavoxelNode(getAttributeValue(state.base.attribute));
|
||||
|
||||
} else {
|
||||
MetavoxelNode* newNode = new MetavoxelNode(getAttributeValue(state.base.attribute));
|
||||
if (state.shouldSubdivide()) {
|
||||
if (!state.shouldSubdivideReference()) {
|
||||
bool leaf;
|
||||
state.base.stream >> leaf;
|
||||
if (leaf) {
|
||||
return isLeaf() ? this : new MetavoxelNode(getAttributeValue(state.base.attribute));
|
||||
|
||||
} else {
|
||||
MetavoxelNode* newNode = new MetavoxelNode(getAttributeValue(state.base.attribute));
|
||||
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
nextState.setMinimum(state.minimum, i);
|
||||
newNode->_children[i] = new MetavoxelNode(state.base.attribute);
|
||||
newNode->_children[i]->read(nextState);
|
||||
}
|
||||
return newNode;
|
||||
}
|
||||
} else if (!isLeaf()) {
|
||||
MetavoxelNode* node = this;
|
||||
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
nextState.setMinimum(state.minimum, i);
|
||||
newNode->_children[i] = new MetavoxelNode(state.base.attribute);
|
||||
newNode->_children[i]->read(nextState);
|
||||
}
|
||||
return newNode;
|
||||
}
|
||||
} else if (!isLeaf()) {
|
||||
MetavoxelNode* node = this;
|
||||
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
|
||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||
nextState.setMinimum(state.minimum, i);
|
||||
if (nextState.becameSubdivided()) {
|
||||
MetavoxelNode* child = _children[i]->readSubdivision(nextState);
|
||||
if (child != _children[i]) {
|
||||
if (node == this) {
|
||||
node = new MetavoxelNode(state.base.attribute, this);
|
||||
if (nextState.becameSubdividedOrCollapsed()) {
|
||||
MetavoxelNode* child = _children[i]->readSubdivision(nextState);
|
||||
if (child != _children[i]) {
|
||||
if (node == this) {
|
||||
node = new MetavoxelNode(state.base.attribute, this);
|
||||
}
|
||||
node->_children[i] = child;
|
||||
_children[i]->decrementReferenceCount(state.base.attribute);
|
||||
}
|
||||
node->_children[i] = child;
|
||||
_children[i]->decrementReferenceCount(state.base.attribute);
|
||||
}
|
||||
}
|
||||
if (node != this) {
|
||||
node->mergeChildren(state.base.attribute, true);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
if (node != this) {
|
||||
node->mergeChildren(state.base.attribute, true);
|
||||
}
|
||||
return node;
|
||||
} else if (!isLeaf()) {
|
||||
return new MetavoxelNode(getAttributeValue(state.base.attribute));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -1879,20 +1914,27 @@ void MetavoxelRendererImplementation::init(MetavoxelRenderer* renderer) {
|
|||
|
||||
void MetavoxelRendererImplementation::augment(MetavoxelData& data, const MetavoxelData& previous,
|
||||
MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
void MetavoxelRendererImplementation::simulate(MetavoxelData& data, float deltaTime,
|
||||
MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
void MetavoxelRendererImplementation::render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod) {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
QByteArray MetavoxelRenderer::getImplementationClassName() const {
|
||||
return "MetavoxelRendererImplementation";
|
||||
}
|
||||
|
||||
PointMetavoxelRenderer::PointMetavoxelRenderer() {
|
||||
DefaultMetavoxelRenderer::DefaultMetavoxelRenderer() {
|
||||
}
|
||||
|
||||
QByteArray PointMetavoxelRenderer::getImplementationClassName() const {
|
||||
return "PointMetavoxelRendererImplementation";
|
||||
QByteArray DefaultMetavoxelRenderer::getImplementationClassName() const {
|
||||
return "DefaultMetavoxelRendererImplementation";
|
||||
}
|
||||
|
||||
const float DEFAULT_PLACEMENT_GRANULARITY = 0.01f;
|
||||
|
|
|
@ -55,6 +55,11 @@ public:
|
|||
|
||||
/// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference.
|
||||
bool becameSubdivided(const glm::vec3& minimum, float size, const MetavoxelLOD& reference, float multiplier = 1.0f) const;
|
||||
|
||||
/// Checks whether the node or any of the nodes underneath it have had subdivision
|
||||
/// enabled or disabled as compared to the reference.
|
||||
bool becameSubdividedOrCollapsed(const glm::vec3& minimum, float size,
|
||||
const MetavoxelLOD& reference, float multiplier = 1.0f) const;
|
||||
};
|
||||
|
||||
DECLARE_STREAMABLE_METATYPE(MetavoxelLOD)
|
||||
|
@ -181,6 +186,7 @@ public:
|
|||
bool shouldSubdivide() const;
|
||||
bool shouldSubdivideReference() const;
|
||||
bool becameSubdivided() const;
|
||||
bool becameSubdividedOrCollapsed() const;
|
||||
|
||||
void setMinimum(const glm::vec3& lastMinimum, int index);
|
||||
};
|
||||
|
@ -555,6 +561,7 @@ public:
|
|||
|
||||
virtual void init(MetavoxelRenderer* renderer);
|
||||
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);
|
||||
|
||||
protected:
|
||||
|
@ -562,13 +569,13 @@ protected:
|
|||
MetavoxelRenderer* _renderer;
|
||||
};
|
||||
|
||||
/// Renders metavoxels as points.
|
||||
class PointMetavoxelRenderer : public MetavoxelRenderer {
|
||||
/// The standard, usual renderer.
|
||||
class DefaultMetavoxelRenderer : public MetavoxelRenderer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Q_INVOKABLE PointMetavoxelRenderer();
|
||||
Q_INVOKABLE DefaultMetavoxelRenderer();
|
||||
|
||||
virtual QByteArray getImplementationClassName() const;
|
||||
};
|
||||
|
|
|
@ -401,6 +401,12 @@ BaseVec3Editor::BaseVec3Editor(QWidget* parent) : QWidget(parent) {
|
|||
layout->addWidget(_z = createComponentBox());
|
||||
}
|
||||
|
||||
void BaseVec3Editor::setSingleStep(double singleStep) {
|
||||
_x->setSingleStep(singleStep);
|
||||
_y->setSingleStep(singleStep);
|
||||
_z->setSingleStep(singleStep);
|
||||
}
|
||||
|
||||
QDoubleSpinBox* BaseVec3Editor::createComponentBox() {
|
||||
QDoubleSpinBox* box = new QDoubleSpinBox();
|
||||
box->setMinimum(-FLT_MAX);
|
||||
|
@ -411,9 +417,7 @@ QDoubleSpinBox* BaseVec3Editor::createComponentBox() {
|
|||
}
|
||||
|
||||
Vec3Editor::Vec3Editor(QWidget* parent) : BaseVec3Editor(parent) {
|
||||
_x->setSingleStep(0.01);
|
||||
_y->setSingleStep(0.01);
|
||||
_z->setSingleStep(0.01);
|
||||
setSingleStep(0.01);
|
||||
}
|
||||
|
||||
static void setComponentValue(QDoubleSpinBox* box, double value) {
|
||||
|
|
|
@ -153,6 +153,8 @@ public:
|
|||
|
||||
BaseVec3Editor(QWidget* parent);
|
||||
|
||||
void setSingleStep(double singleStep);
|
||||
|
||||
protected slots:
|
||||
|
||||
virtual void updateValue() = 0;
|
||||
|
@ -175,6 +177,8 @@ public:
|
|||
|
||||
Vec3Editor(QWidget* parent);
|
||||
|
||||
const glm::vec3& getValue() const { return _value; }
|
||||
|
||||
signals:
|
||||
|
||||
void valueChanged(const glm::vec3& vector);
|
||||
|
|
Loading…
Reference in a new issue